diff options
Diffstat (limited to 'kradio3/plugins/timeshifter')
18 files changed, 1291 insertions, 0 deletions
diff --git a/kradio3/plugins/timeshifter/Makefile.am b/kradio3/plugins/timeshifter/Makefile.am new file mode 100644 index 0000000..23da794 --- /dev/null +++ b/kradio3/plugins/timeshifter/Makefile.am @@ -0,0 +1,18 @@ +SUBDIRS = po icons . + +INCLUDES = $(all_includes) +METASOURCES = AUTO + +libkradio_LTLIBRARIES = libtimeshifter.la +libtimeshifter_la_SOURCES = timeshifter.cpp timeshifter-configuration-ui.ui \ + timeshifter-configuration.cpp +libtimeshifter_la_LDFLAGS = -module -avoid-version $(KDE_RPATH) $(all_libraries) + +noinst_HEADERS = timeshifter.h timeshifter-configuration.h + +#messages: rc.cpp +# $(XGETTEXT) *.cpp *.h -o po/kradio-timeshifter.pot + +messages: rc.cpp + $(EXTRACTRC) *.rc *.ui >> rc.cpp + $(XGETTEXT) rc.cpp *.h *.cpp -o po/kradio-timeshifter.pot diff --git a/kradio3/plugins/timeshifter/icons/Makefile.am b/kradio3/plugins/timeshifter/icons/Makefile.am new file mode 100644 index 0000000..b3f2583 --- /dev/null +++ b/kradio3/plugins/timeshifter/icons/Makefile.am @@ -0,0 +1,2 @@ +icons_ICON = AUTO +iconsdir = $(kde_datadir)/kradio/icons diff --git a/kradio3/plugins/timeshifter/icons/hi16-action-kradio_pause.png b/kradio3/plugins/timeshifter/icons/hi16-action-kradio_pause.png Binary files differnew file mode 100644 index 0000000..a5f47a7 --- /dev/null +++ b/kradio3/plugins/timeshifter/icons/hi16-action-kradio_pause.png diff --git a/kradio3/plugins/timeshifter/icons/hi16-app-kradio_plus_pause.png b/kradio3/plugins/timeshifter/icons/hi16-app-kradio_plus_pause.png Binary files differnew file mode 100644 index 0000000..320ba48 --- /dev/null +++ b/kradio3/plugins/timeshifter/icons/hi16-app-kradio_plus_pause.png diff --git a/kradio3/plugins/timeshifter/icons/hi22-action-kradio_pause.png b/kradio3/plugins/timeshifter/icons/hi22-action-kradio_pause.png Binary files differnew file mode 100644 index 0000000..69dfb9e --- /dev/null +++ b/kradio3/plugins/timeshifter/icons/hi22-action-kradio_pause.png diff --git a/kradio3/plugins/timeshifter/icons/hi22-app-kradio_plus_pause.png b/kradio3/plugins/timeshifter/icons/hi22-app-kradio_plus_pause.png Binary files differnew file mode 100644 index 0000000..31a25ac --- /dev/null +++ b/kradio3/plugins/timeshifter/icons/hi22-app-kradio_plus_pause.png diff --git a/kradio3/plugins/timeshifter/icons/hi32-action-kradio_pause.png b/kradio3/plugins/timeshifter/icons/hi32-action-kradio_pause.png Binary files differnew file mode 100644 index 0000000..8425c1c --- /dev/null +++ b/kradio3/plugins/timeshifter/icons/hi32-action-kradio_pause.png diff --git a/kradio3/plugins/timeshifter/icons/hi32-app-kradio_plus_pause.png b/kradio3/plugins/timeshifter/icons/hi32-app-kradio_plus_pause.png Binary files differnew file mode 100644 index 0000000..9b42662 --- /dev/null +++ b/kradio3/plugins/timeshifter/icons/hi32-app-kradio_plus_pause.png diff --git a/kradio3/plugins/timeshifter/icons/hi48-app-kradio_plus_pause.png b/kradio3/plugins/timeshifter/icons/hi48-app-kradio_plus_pause.png Binary files differnew file mode 100644 index 0000000..efaa1e1 --- /dev/null +++ b/kradio3/plugins/timeshifter/icons/hi48-app-kradio_plus_pause.png diff --git a/kradio3/plugins/timeshifter/icons/hi64-app-kradio_plus_pause.png b/kradio3/plugins/timeshifter/icons/hi64-app-kradio_plus_pause.png Binary files differnew file mode 100644 index 0000000..fdd0aaf --- /dev/null +++ b/kradio3/plugins/timeshifter/icons/hi64-app-kradio_plus_pause.png diff --git a/kradio3/plugins/timeshifter/po/Makefile.am b/kradio3/plugins/timeshifter/po/Makefile.am new file mode 100644 index 0000000..276fe4b --- /dev/null +++ b/kradio3/plugins/timeshifter/po/Makefile.am @@ -0,0 +1,3 @@ + +PACKAGE = kradio-timeshifter +POFILES = AUTO diff --git a/kradio3/plugins/timeshifter/po/de.po b/kradio3/plugins/timeshifter/po/de.po new file mode 100644 index 0000000..66806f9 --- /dev/null +++ b/kradio3/plugins/timeshifter/po/de.po @@ -0,0 +1,91 @@ +# translation of de.po to +# translation of kradio-timeshifter.po to +# This file is put in the public domain. +# +# Ernst Martin Witte <[email protected]>, 2006. +msgid "" +msgstr "" +"Project-Id-Version: de\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2006-11-12 18:41+0100\n" +"PO-Revision-Date: 2006-11-12 18:24+0100\n" +"Last-Translator: Ernst Martin Witte <[email protected]>\n" +"Language-Team: <[email protected]>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: KBabel 1.11.4\n" + +#. i18n: file timeshifter-configuration-ui.ui line 16 +#: rc.cpp:3 rc.cpp:22 timeshifter-configuration-ui.cpp:118 +#, no-c-format +msgid "SetupDialogGeneral" +msgstr "SetupDialogGeneral" + +#. i18n: file timeshifter-configuration-ui.ui line 72 +#: rc.cpp:6 rc.cpp:25 timeshifter-configuration-ui.cpp:119 +#, no-c-format +msgid " MB" +msgstr " MB" + +#. i18n: file timeshifter-configuration-ui.ui line 92 +#: rc.cpp:9 rc.cpp:28 timeshifter-configuration-ui.cpp:120 +#, no-c-format +msgid "Maximum File Size" +msgstr "maximale Dateigröße" + +#. i18n: file timeshifter-configuration-ui.ui line 100 +#: rc.cpp:12 rc.cpp:31 timeshifter-configuration-ui.cpp:121 +#, no-c-format +msgid "Playback Mixer Device" +msgstr "Wiedergabemixer" + +#. i18n: file timeshifter-configuration-ui.ui line 108 +#: rc.cpp:15 rc.cpp:34 timeshifter-configuration-ui.cpp:122 +#, no-c-format +msgid "Temporary File" +msgstr "Temporäre Datei" + +#. i18n: file timeshifter-configuration-ui.ui line 121 +#: rc.cpp:18 rc.cpp:37 timeshifter-configuration-ui.cpp:123 +#, no-c-format +msgid "Playback Mixer Channel" +msgstr "Wiedergabemixerkanal" + +#: timeshifter-configuration.cpp:138 +msgid "any ( * )" +msgstr "alle ( * )" + +#: timeshifter-configuration.cpp:140 +msgid "TimeShifter Temporary File Selection" +msgstr "Auswahl der temporären Datei für die zeitversetzte Wiedergabe" + +#: timeshifter-configuration.cpp:143 +msgid "Select TimeShifter Temporary File" +msgstr "Auswahl der temporären Datei für die zeitversetzte Wiedergabe" + +#: timeshifter.cpp:31 +msgid "TimeShift Support" +msgstr "Unterstützung für die Zeitversetzte Wiedergabe" + +#: timeshifter.cpp:36 +msgid "TimeShifter Plugin" +msgstr "Plugin für die zeitversetzte Wiedergabe" + +#: timeshifter.cpp:121 +msgid "Timeshifter" +msgstr "Zeitversetzte Wiedergabe" + +#: timeshifter.cpp:122 +msgid "Timeshifter Options" +msgstr "Optionen für die zeitversetzte Wiedergabe" + +#: timeshifter.cpp:355 +msgid "TimeShifter::notifySoundStreamData: clients skipped %1 bytes. Data Lost" +msgstr "" +"TimeShifter::notifySoundStreamData: %1 bytes der Sounddaten wurden ignoriert " +"und sind verloren" + +#: timeshifter.cpp:412 +msgid "internal stream, not stored" +msgstr "interner, nicht aufgezeichneter Datenstrom" diff --git a/kradio3/plugins/timeshifter/po/ru.po b/kradio3/plugins/timeshifter/po/ru.po new file mode 100644 index 0000000..1f692e3 --- /dev/null +++ b/kradio3/plugins/timeshifter/po/ru.po @@ -0,0 +1,91 @@ +# translation of ru.po to +# translation of kradio-timeshifter.po to +# This file is put in the public domain. +# Алексей Кузнецов <[email protected]>, 2006. +# +msgid "" +msgstr "" +"Project-Id-Version: ru\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2006-11-12 18:20+0100\n" +"PO-Revision-Date: 2006-11-08 12:19+0300\n" +"Last-Translator: Алексей Кузнецов <[email protected]>\n" +"Language-Team: <[email protected]>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: KBabel 1.10\n" + +#. i18n: file timeshifter-configuration-ui.ui line 16 +#: rc.cpp:3 rc.cpp:22 timeshifter-configuration-ui.cpp:118 +#, no-c-format +msgid "SetupDialogGeneral" +msgstr "SetupDialogGeneral" + +#. i18n: file timeshifter-configuration-ui.ui line 72 +#: rc.cpp:6 rc.cpp:25 timeshifter-configuration-ui.cpp:119 +#, no-c-format +msgid " MB" +msgstr " Мб" + +#. i18n: file timeshifter-configuration-ui.ui line 92 +#: rc.cpp:9 rc.cpp:28 timeshifter-configuration-ui.cpp:120 +#, no-c-format +msgid "Maximum File Size" +msgstr "Размер файла, не более" + +#. i18n: file timeshifter-configuration-ui.ui line 100 +#: rc.cpp:12 rc.cpp:31 timeshifter-configuration-ui.cpp:121 +#, no-c-format +msgid "Playback Mixer Device" +msgstr "Устройство воспроизведения" + +#. i18n: file timeshifter-configuration-ui.ui line 108 +#: rc.cpp:15 rc.cpp:34 timeshifter-configuration-ui.cpp:122 +#, no-c-format +msgid "Temporary File" +msgstr "Временный файл" + +#. i18n: file timeshifter-configuration-ui.ui line 121 +#: rc.cpp:18 rc.cpp:37 timeshifter-configuration-ui.cpp:123 +#, no-c-format +msgid "Playback Mixer Channel" +msgstr "Канал воспроизведения" + +#: timeshifter-configuration.cpp:138 +msgid "any ( * )" +msgstr "Все ( * )" + +#: timeshifter-configuration.cpp:140 +msgid "TimeShifter Temporary File Selection" +msgstr "Выбор временного файла для реализации приостановки вещания" + +#: timeshifter-configuration.cpp:143 +msgid "Select TimeShifter Temporary File" +msgstr "Выберите временный файл" + +#: timeshifter.cpp:31 +msgid "TimeShift Support" +msgstr "Поддержка приостановки вещания" + +#: timeshifter.cpp:36 +msgid "TimeShifter Plugin" +msgstr "Модуль приостановки вещания" + +#: timeshifter.cpp:121 +msgid "Timeshifter" +msgstr "Приостановка" + +#: timeshifter.cpp:122 +msgid "Timeshifter Options" +msgstr "Параметры приостановки вещания" + +#: timeshifter.cpp:355 +msgid "TimeShifter::notifySoundStreamData: clients skipped %1 bytes. Data Lost" +msgstr "" +"TimeShifter::notifySoundStreamData: клиенты пропустили %1 байт. Данные " +"утеряны." + +#: timeshifter.cpp:412 +msgid "internal stream, not stored" +msgstr "" diff --git a/kradio3/plugins/timeshifter/timeshifter-configuration-ui.ui b/kradio3/plugins/timeshifter/timeshifter-configuration-ui.ui new file mode 100644 index 0000000..3e3f4e4 --- /dev/null +++ b/kradio3/plugins/timeshifter/timeshifter-configuration-ui.ui @@ -0,0 +1,225 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>TimeShifterConfigurationUI</class> +<widget class="QWidget"> + <property name="name"> + <cstring>TimeShifterConfigurationUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>299</width> + <height>134</height> + </rect> + </property> + <property name="caption"> + <string>SetupDialogGeneral</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer row="1" column="3"> + <property name="name"> + <cstring>spacer18_3_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Preferred</enum> + </property> + <property name="sizeHint"> + <size> + <width>30</width> + <height>21</height> + </size> + </property> + </spacer> + <spacer row="1" column="1"> + <property name="name"> + <cstring>spacer18_3_2_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Preferred</enum> + </property> + <property name="sizeHint"> + <size> + <width>104</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QSpinBox" row="1" column="2"> + <property name="name"> + <cstring>editTempFileSize</cstring> + </property> + <property name="suffix"> + <string> MB</string> + </property> + <property name="maxValue"> + <number>10000</number> + </property> + <property name="minValue"> + <number>10</number> + </property> + <property name="lineStep"> + <number>10</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>labelTempFileSize</cstring> + </property> + <property name="text"> + <string>Maximum File Size</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>labelPlaybackMixerDevice</cstring> + </property> + <property name="text"> + <string>Playback Mixer Device</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>labelTempFile</cstring> + </property> + <property name="text"> + <string>Temporary File</string> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>editTempFile</cstring> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>labelPlaybackMixerChannel</cstring> + </property> + <property name="text"> + <string>Playback Mixer Channel</string> + </property> + </widget> + <widget class="QPushButton" row="0" column="3"> + <property name="name"> + <cstring>buttonSelectTempFile</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>26</width> + <height>26</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="iconSet"> + <iconset>"fileopen"</iconset> + </property> + </widget> + <widget class="QComboBox" row="2" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>comboPlaybackMixerDevice</cstring> + </property> + </widget> + <widget class="QComboBox" row="3" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>comboPlaybackMixerChannel</cstring> + </property> + </widget> + <spacer row="3" column="3"> + <property name="name"> + <cstring>spacer18_3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Preferred</enum> + </property> + <property name="sizeHint"> + <size> + <width>30</width> + <height>21</height> + </size> + </property> + </spacer> + <spacer row="2" column="3"> + <property name="name"> + <cstring>spacer18_3_4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Preferred</enum> + </property> + <property name="sizeHint"> + <size> + <width>30</width> + <height>21</height> + </size> + </property> + </spacer> + </grid> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer16</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>61</height> + </size> + </property> + </spacer> + </grid> +</widget> +<tabstops> + <tabstop>editTempFile</tabstop> + <tabstop>buttonSelectTempFile</tabstop> + <tabstop>comboPlaybackMixerChannel</tabstop> +</tabstops> +<includes> + <include location="global" impldecl="in implementation">kiconloader.h</include> + <include location="global" impldecl="in implementation">knuminput.h</include> +</includes> +<pixmapfunction>SmallIconSet</pixmapfunction> +<layoutdefaults spacing="6" margin="0"/> +</UI> diff --git a/kradio3/plugins/timeshifter/timeshifter-configuration.cpp b/kradio3/plugins/timeshifter/timeshifter-configuration.cpp new file mode 100644 index 0000000..828cc33 --- /dev/null +++ b/kradio3/plugins/timeshifter/timeshifter-configuration.cpp @@ -0,0 +1,203 @@ +/*************************************************************************** + v4lradio-configuration.cpp - description + ------------------- + begin : Fre Jun 20 2003 + copyright : (C) 2003 by Martin Witte + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include <sys/ioctl.h> +#include <fcntl.h> +#include <unistd.h> +#include <linux/soundcard.h> + +#include <qspinbox.h> +#include <qlineedit.h> +#include <qcombobox.h> +#include <qlabel.h> +#include <qfile.h> +#include <qpushbutton.h> + +#include <kfiledialog.h> +#include <knuminput.h> +#include <klocale.h> + +#include "../../src/include/utils.h" +#include "../../src/include/gui_list_helper.h" +#include "timeshifter-configuration.h" +#include "timeshifter.h" + +TimeShifterConfiguration::TimeShifterConfiguration (QWidget *parent, TimeShifter *shifter) + : TimeShifterConfigurationUI(parent), + m_ignoreGUIChanges(false), + m_myControlChange(0), + m_PlaybackMixerHelper(comboPlaybackMixerDevice, StringListHelper::SORT_BY_DESCR), + m_PlaybackChannelHelper(comboPlaybackMixerChannel), + m_Shifter(shifter), + m_dirty(true) +{ + QObject::connect(buttonSelectTempFile, SIGNAL(clicked()), + this, SLOT(selectTempFile())); + QObject::connect(comboPlaybackMixerDevice, SIGNAL(activated(int)), + this, SLOT(slotComboPlaybackMixerSelected(int))); + + connect(editTempFile, SIGNAL(textChanged(const QString&)), this, SLOT(slotSetDirty())); + connect(editTempFileSize, SIGNAL(valueChanged(int)), this, SLOT(slotSetDirty())); + connect(comboPlaybackMixerChannel, SIGNAL(activated( int )), this, SLOT(slotSetDirty())); + connect(comboPlaybackMixerDevice, SIGNAL(activated( int )), this, SLOT(slotSetDirty())); + slotCancel(); +} + + +TimeShifterConfiguration::~TimeShifterConfiguration () +{ +} + + +bool TimeShifterConfiguration::connectI (Interface *i) +{ + bool a = ISoundStreamClient::connectI(i); + return a; +} + + +bool TimeShifterConfiguration::disconnectI (Interface *i) +{ + bool a = ISoundStreamClient::disconnectI(i); + return a; +} + +void TimeShifterConfiguration::noticeConnectedI (ISoundStreamServer *s, bool pointer_valid) +{ + ISoundStreamClient::noticeConnectedI(s, pointer_valid); + if (s && pointer_valid) { + s->register4_notifyPlaybackChannelsChanged(this); + } +} + +void TimeShifterConfiguration::noticeConnectedSoundClient(ISoundStreamClient::thisInterface *i, bool pointer_valid) +{ + if (i && pointer_valid && i->supportsPlayback() && m_Shifter) { + const QString &org_mid = m_Shifter->getPlaybackMixer(); + bool org_present = m_PlaybackMixerHelper.contains(org_mid); + const QString &mid = org_present ? m_PlaybackMixerHelper.getCurrentItem() : org_mid; + const QString &org_ch = m_Shifter->getPlaybackMixerChannel(); + const QString &ch = org_present ? m_PlaybackChannelHelper.getCurrentText() : org_ch; + setPlaybackMixer(mid, ch); + } +} + + +void TimeShifterConfiguration::noticeDisconnectedSoundClient(ISoundStreamClient::thisInterface *i, bool pointer_valid) +{ + if (i && pointer_valid && i->supportsPlayback()) { + setPlaybackMixer(m_Shifter->getPlaybackMixer(), m_Shifter->getPlaybackMixerChannel()); + } +} + + + +bool TimeShifterConfiguration::setPlaybackMixer(const QString &_mixer_id, const QString &Channel) +{ + QString mixer_id = _mixer_id; + bool old = m_ignoreGUIChanges; + m_ignoreGUIChanges = true; + + m_PlaybackMixerHelper.setData(getPlaybackClientDescriptions()); + m_PlaybackMixerHelper.setCurrentItem(mixer_id); + mixer_id = m_PlaybackMixerHelper.getCurrentItem(); + + ISoundStreamClient *mixer = getSoundStreamClientWithID(mixer_id); + if (mixer) { + m_PlaybackChannelHelper.setData(mixer->getPlaybackChannels()); + m_PlaybackChannelHelper.setCurrentText(m_PlaybackChannelHelper.contains(Channel) ? Channel : m_Shifter->getPlaybackMixerChannel()); + } + labelPlaybackMixerChannel->setEnabled(mixer != NULL); + comboPlaybackMixerChannel->setEnabled(mixer != NULL); + + m_ignoreGUIChanges = old; + return true; +} + + +// GUI Slots + + +void TimeShifterConfiguration::selectTempFile() +{ + KFileDialog fd("/tmp/", + i18n("any ( * )").ascii(), + this, + i18n("TimeShifter Temporary File Selection").ascii(), + TRUE); + fd.setMode(KFile::File); + fd.setCaption (i18n("Select TimeShifter Temporary File")); + + if (fd.exec() == QDialog::Accepted) { + editTempFile->setText(fd.selectedFile()); + } +} + + +void TimeShifterConfiguration::slotComboPlaybackMixerSelected(int /*idx*/) +{ + if (m_ignoreGUIChanges) return; + setPlaybackMixer(m_PlaybackMixerHelper.getCurrentItem(), m_PlaybackChannelHelper.getCurrentText()); +} + + +void TimeShifterConfiguration::slotOK() +{ + if (m_Shifter && m_dirty) { + m_Shifter->setTempFile(editTempFile->text(), editTempFileSize->value() * (Q_UINT64)(1024 * 1024)); + m_Shifter->setPlaybackMixer(m_PlaybackMixerHelper.getCurrentItem(), + m_PlaybackChannelHelper.getCurrentText()); + m_dirty = false; + } +} + + +void TimeShifterConfiguration::slotCancel() +{ + if (m_Shifter && m_dirty) { + editTempFile->setText(m_Shifter->getTempFileName()); + editTempFileSize->setValue(m_Shifter->getTempFileMaxSize() / 1024 / 1024); + + setPlaybackMixer(m_Shifter->getPlaybackMixer(), m_Shifter->getPlaybackMixerChannel()); + m_dirty = false; + } +} + + +bool TimeShifterConfiguration::noticePlaybackChannelsChanged(const QString & client_id, const QStringList &/*channels*/) +{ + if (m_PlaybackMixerHelper.getCurrentItem() == client_id) { + setPlaybackMixer(client_id, m_PlaybackChannelHelper.getCurrentText()); + } + return true; +} + + +void TimeShifterConfiguration::slotSetDirty() +{ + if (!m_ignoreGUIChanges) { + m_dirty = true; + } +} + +void TimeShifterConfiguration::slotUpdateConfig() +{ + slotSetDirty(); + slotCancel(); +} + +#include "timeshifter-configuration.moc" diff --git a/kradio3/plugins/timeshifter/timeshifter-configuration.h b/kradio3/plugins/timeshifter/timeshifter-configuration.h new file mode 100644 index 0000000..7d34c3d --- /dev/null +++ b/kradio3/plugins/timeshifter/timeshifter-configuration.h @@ -0,0 +1,83 @@ +/*************************************************************************** + v4lradio-configuration.h - description + ------------------- + begin : Fre Jun 20 2003 + copyright : (C) 2003 by Martin Witte + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef KRADIO_TIMESHIFTER_CONFIGURATION_H +#define KRADIO_TIMESHIFTER_CONFIGURATION_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "../../src/include/soundstreamclient_interfaces.h" +#include "../../src/include/gui_list_helper.h" + +#include "timeshifter-configuration-ui.h" + +class QWidget; +class TimeShifter; + +class TimeShifterConfiguration : public TimeShifterConfigurationUI, + public ISoundStreamClient +{ +Q_OBJECT +public : + TimeShifterConfiguration (QWidget *parent, TimeShifter *shifter); + ~TimeShifterConfiguration (); + + bool connectI (Interface *i); + bool disconnectI (Interface *i); + + void noticeConnectedSoundClient(ISoundStreamClient::thisInterface *i, bool pointer_valid); + void noticeDisconnectedSoundClient(ISoundStreamClient::thisInterface *i, bool pointer_valid); + +// ISoundStreamClient + +RECEIVERS: + void noticeConnectedI (ISoundStreamServer *s, bool pointer_valid); + bool noticePlaybackChannelsChanged(const QString & /*client_id*/, const QStringList &/*channels*/); + +protected: + + bool setPlaybackMixer(const QString &_mixer_id, const QString &Channel); + + +protected slots: + + void selectTempFile(); + void slotComboPlaybackMixerSelected(int idx); + + void slotOK(); + void slotCancel(); + void slotSetDirty(); + void slotUpdateConfig(); + +protected: + + bool m_ignoreGUIChanges; + int m_myControlChange; + + typedef GUIListHelper<QComboBox, QString> StringListHelper; + typedef GUISimpleListHelper<QComboBox> ChannelListHelper; + + StringListHelper m_PlaybackMixerHelper; + ChannelListHelper m_PlaybackChannelHelper; + + TimeShifter *m_Shifter; + bool m_dirty; +}; + +#endif diff --git a/kradio3/plugins/timeshifter/timeshifter.cpp b/kradio3/plugins/timeshifter/timeshifter.cpp new file mode 100644 index 0000000..bff1851 --- /dev/null +++ b/kradio3/plugins/timeshifter/timeshifter.cpp @@ -0,0 +1,455 @@ +/*************************************************************************** + timeshifter.cpp - description + ------------------- + begin : Mon May 16 13:39:31 CEST 2005 + copyright : (C) 2005 by Ernst Martin Witte + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <klocale.h> +#include <linux/soundcard.h> + +#include "../../src/include/utils.h" +#include "timeshifter.h" +#include "timeshifter-configuration.h" + +/////////////////////////////////////////////////////////////////////// + +PLUGIN_LIBRARY_FUNCTIONS(TimeShifter, "kradio-timeshifter", i18n("TimeShift Support")); + +/////////////////////////////////////////////////////////////////////// + +TimeShifter::TimeShifter (const QString &name) + : PluginBase(name, i18n("TimeShifter Plugin")), + m_TempFileName("/tmp/kradio-timeshifter-tempfile"), + m_TempFileMaxSize(256*1024*1024), + m_PlaybackMixerID(QString::null), + m_PlaybackMixerChannel("PCM"), + m_orgVolume(0.0), + m_PlaybackMetaData(0,0,0), + m_PlaybackDataLeftInBuffer(0), + m_RingBuffer(m_TempFileName, m_TempFileMaxSize) +{ +} + + +TimeShifter::~TimeShifter () +{ +} + + +bool TimeShifter::connectI (Interface *i) +{ + bool a = PluginBase::connectI(i); + bool b = ISoundStreamClient::connectI(i); + return a || b; +} + + +bool TimeShifter::disconnectI (Interface *i) +{ + bool a = PluginBase::disconnectI(i); + bool b = ISoundStreamClient::disconnectI(i); + return a || b; +} + + +void TimeShifter::noticeConnectedI (ISoundStreamServer *s, bool pointer_valid) +{ + ISoundStreamClient::noticeConnectedI(s, pointer_valid); + if (s && pointer_valid) { + s->register4_notifySoundStreamClosed(this); + s->register4_sendStartPlayback(this); + s->register4_sendStopPlayback(this); + s->register4_sendPausePlayback(this); + s->register4_notifySoundStreamData(this); + s->register4_notifyReadyForPlaybackData(this); + s->register4_querySoundStreamDescription(this); + s->register4_sendStartCaptureWithFormat(this); + s->register4_sendStopCapture(this); + } +} + + +void TimeShifter::saveState (KConfig *config) const +{ + config->setGroup(QString("timeshifter-") + name()); + + config->writeEntry("temp-file-name", m_TempFileName); + config->writeEntry("max-file-size", m_TempFileMaxSize / 1024 / 1024); + + config->writeEntry("PlaybackMixerID", m_PlaybackMixerID); + config->writeEntry("PlaybackMixerChannel", m_PlaybackMixerChannel); +} + + +void TimeShifter::restoreState (KConfig *config) +{ + config->setGroup(QString("timeshifter-") + name()); + + QString fname = config->readEntry("temp-file-name", "/tmp/kradio-timeshifter-tempfile"); + Q_UINT64 fsize = 1024 * 1024 * config->readNumEntry("max-file-size", 256); + + QString mixerID = config->readEntry ("PlaybackMixerID", QString::null); + QString channel = config->readEntry ("PlaybackMixerChannel", "PCM"); + + setPlaybackMixer(mixerID, channel); + setTempFile(fname, fsize); + + emit sigUpdateConfig(); +} + + +ConfigPageInfo TimeShifter::createConfigurationPage() +{ + TimeShifterConfiguration *conf = new TimeShifterConfiguration(NULL, this); + QObject::connect(this, SIGNAL(sigUpdateConfig()), conf, SLOT(slotUpdateConfig())); + return ConfigPageInfo (conf, + i18n("Timeshifter"), + i18n("Timeshifter Options"), + "kradio_pause"); +} + +AboutPageInfo TimeShifter::createAboutPage() +{ + return AboutPageInfo(); +} + + +bool TimeShifter::noticeSoundStreamClosed(SoundStreamID id) +{ + return stopPlayback(id); +} + +bool TimeShifter::startPlayback(SoundStreamID id) +{ + if (id == m_OrgStreamID) { + m_StreamPaused = false; + return true; + } + return false; +} + +bool TimeShifter::stopPlayback(SoundStreamID id) +{ + if (id == m_NewStreamID) { + + return sendStopPlayback(m_OrgStreamID); + + } else if (id == m_OrgStreamID) { + + SoundStreamID tmp_newID = m_NewStreamID; + SoundStreamID tmp_orgID = m_OrgStreamID; + + m_OrgStreamID.invalidate(); + m_NewStreamID.invalidate(); + + sendStopCapture(tmp_newID); + closeSoundStream(tmp_newID); + stopPlayback(tmp_newID); + m_RingBuffer.clear(); + m_PlaybackMetaData = SoundMetaData(0,0,0); + m_PlaybackDataLeftInBuffer = 0; + return true; + } + return false; +} + + +bool TimeShifter::pausePlayback(SoundStreamID id) +{ + if (!m_OrgStreamID.isValid()) { + SoundStreamID orgid = id; + SoundStreamID newid = createNewSoundStream(orgid, false); + m_OrgStreamID = orgid; + m_NewStreamID = newid; + notifySoundStreamCreated(newid); + notifySoundStreamRedirected(orgid, newid); + queryPlaybackVolume(newid, m_orgVolume); + sendMute(newid); + sendPlaybackVolume(newid, 0); + + m_NewStreamID.invalidate(); + sendStopPlayback(newid); + m_NewStreamID = newid; + + m_StreamPaused = true; + + m_RingBuffer.clear(); + m_PlaybackMetaData = SoundMetaData(0,0,0); + m_PlaybackDataLeftInBuffer = 0; + + sendStartCaptureWithFormat(m_NewStreamID, m_SoundFormat, m_realSoundFormat); + + ISoundStreamClient *playback_mixer = searchPlaybackMixer(); + if (playback_mixer) { + playback_mixer->preparePlayback(m_OrgStreamID, m_PlaybackMixerChannel, /*active*/true, /*startimmediately*/ true); + m_PlaybackMixerID = playback_mixer->getSoundStreamClientID(); + } + + return true; + + } else if (id == m_OrgStreamID) { + m_StreamPaused = !m_StreamPaused; + if (!m_StreamPaused) { +// sendStartPlayback(m_OrgStreamID); + sendUnmute(m_OrgStreamID); + sendPlaybackVolume(m_OrgStreamID, m_orgVolume); + } else { + queryPlaybackVolume(m_OrgStreamID, m_orgVolume); + } + return true; + } + return false; +} + + +size_t TimeShifter::writeMetaDataToBuffer(const SoundMetaData &md, char *buffer, size_t buffer_size) +{ + Q_UINT64 pos = md.position(); + time_t abs = md.absoluteTimestamp(); + time_t rel = md.relativeTimestamp(); + size_t url_len = md.url().url().length() + 1; + size_t req_size = sizeof(req_size) + sizeof(pos) + sizeof(abs) + sizeof(rel) + sizeof(url_len) + url_len; + if (req_size <= buffer_size) { + *(size_t*)buffer = req_size; + buffer += sizeof(req_size); + *(Q_UINT64*)buffer = pos; + buffer += sizeof(pos); + *(time_t*)buffer = abs; + buffer += sizeof(abs); + *(time_t*)buffer = rel; + buffer += sizeof(rel); + *(size_t*)buffer = url_len; + buffer += sizeof(url_len); + memcpy(buffer, md.url().url().ascii(), url_len); + buffer += url_len; + return req_size; + } else if (buffer_size >= sizeof(req_size)) { + *(size_t*)buffer = sizeof(req_size); + return sizeof(req_size); + } else { + return 0; + } +} + +size_t TimeShifter::readMetaDataFromBuffer(SoundMetaData &md, const char *buffer, size_t buffer_size) +{ + size_t req_size = 0; + Q_UINT64 pos = 0; + time_t abs = 0; + time_t rel = 0; + size_t url_len = 0; + KURL url; + if (buffer_size >= sizeof(req_size)) { + req_size = *(size_t*)buffer; + buffer += sizeof(req_size); + if (req_size > sizeof(req_size)) { + pos = *(Q_UINT64*)buffer; + buffer += sizeof(Q_UINT64); + abs = *(time_t*)buffer; + buffer += sizeof(abs); + rel = *(time_t*)buffer; + buffer += sizeof(rel); + url_len = *(size_t*)buffer; + buffer += sizeof(url_len); + url = buffer; + buffer += url_len; + } + } + md = SoundMetaData(pos, rel, abs, url); + return req_size; +} + + +bool TimeShifter::noticeSoundStreamData(SoundStreamID id, const SoundFormat &/*sf*/, const char *data, size_t size, size_t &consumed_size, const SoundMetaData &md) +{ + if (id == m_NewStreamID) { + char buffer_meta[1024]; + size_t meta_buffer_size = writeMetaDataToBuffer(md, buffer_meta, 1024); + size_t packet_size = meta_buffer_size + sizeof(size) + size; + if (packet_size > m_RingBuffer.getMaxSize()) + return false; + Q_INT64 diff = m_RingBuffer.getFreeSize() - packet_size; + while (diff < 0) { + skipPacketInRingBuffer(); + diff = m_RingBuffer.getFreeSize() - packet_size; + } + m_RingBuffer.addData(buffer_meta, meta_buffer_size); + m_RingBuffer.addData((const char*)&size, sizeof(size)); + m_RingBuffer.addData(data, size); + consumed_size = (consumed_size == SIZE_T_DONT_CARE) ? size : min(consumed_size, size); + return true; + } + return false; +} + + +void TimeShifter::skipPacketInRingBuffer() +{ + if (m_PlaybackDataLeftInBuffer > 0) { + m_RingBuffer.removeData(m_PlaybackDataLeftInBuffer); + } else { + size_t meta_size = 0; + m_RingBuffer.takeData((char*)&meta_size, sizeof(meta_size)); + m_RingBuffer.removeData(meta_size - sizeof(meta_size)); + size_t packet_size = 0; + m_RingBuffer.takeData((char*)&packet_size, sizeof(packet_size)); + m_RingBuffer.removeData(packet_size - sizeof(packet_size)); + } +} + + +bool TimeShifter::noticeReadyForPlaybackData(SoundStreamID id, size_t free_size) +{ + if (id == m_OrgStreamID && !m_StreamPaused) { + + while (!m_RingBuffer.error() && m_RingBuffer.getFillSize() > 0 && free_size > 0) { + if (m_PlaybackDataLeftInBuffer == 0) { + char meta_buffer[1024]; + size_t &meta_size = *(size_t*)meta_buffer; + meta_size = 0; + m_RingBuffer.takeData(meta_buffer, sizeof(meta_size)); + if (meta_size && meta_size <= 1024) { + m_RingBuffer.takeData(meta_buffer + sizeof(meta_size), meta_size - sizeof(meta_size)); + readMetaDataFromBuffer(m_PlaybackMetaData, meta_buffer, meta_size); + } else { + m_RingBuffer.removeData(meta_size - sizeof(meta_size)); + } + + m_PlaybackDataLeftInBuffer = 0; + m_RingBuffer.takeData((char*)&m_PlaybackDataLeftInBuffer, sizeof(m_PlaybackDataLeftInBuffer)); + } + + const size_t buffer_size = 65536; + char buffer[buffer_size]; + + while (!m_RingBuffer.error() && m_PlaybackDataLeftInBuffer > 0 && free_size > 0) { + size_t s = m_PlaybackDataLeftInBuffer < free_size ? m_PlaybackDataLeftInBuffer : free_size; + + if (s > buffer_size) + s = buffer_size; + s = m_RingBuffer.takeData(buffer, s); + + size_t consumed_size = SIZE_T_DONT_CARE; + notifySoundStreamData(m_OrgStreamID, m_realSoundFormat, buffer, s, consumed_size, m_PlaybackMetaData); + if (consumed_size == SIZE_T_DONT_CARE) + consumed_size = s; + + free_size -= consumed_size; + m_PlaybackDataLeftInBuffer -= consumed_size; + if (consumed_size < s) { + logError(i18n("TimeShifter::notifySoundStreamData: clients skipped %1 bytes. Data Lost").arg(s - consumed_size)); + free_size = 0; // break condition for outer loop + break; + } + } + } + return true; + } + return false; +} + + + +ISoundStreamClient *TimeShifter::searchPlaybackMixer() +{ + ISoundStreamClient *playback_mixer = getSoundStreamClientWithID(m_PlaybackMixerID); + + // some simple sort of autodetection if one mixer isn't present any more + if (!playback_mixer) { + QPtrList<ISoundStreamClient> playback_mixers = queryPlaybackMixers(); + if (!playback_mixers.isEmpty()) + playback_mixer = playback_mixers.first(); + } + return playback_mixer; +} + + +bool TimeShifter::setPlaybackMixer(const QString &soundStreamClientID, const QString &ch) +{ + m_PlaybackMixerID = soundStreamClientID; + m_PlaybackMixerChannel = ch; + + ISoundStreamClient *playback_mixer = searchPlaybackMixer(); + + float oldVolume; + if (m_OrgStreamID.isValid()) { + queryPlaybackVolume(m_OrgStreamID, oldVolume); + sendStopPlayback(m_OrgStreamID); + sendReleasePlayback(m_OrgStreamID); + } + + if (playback_mixer) + playback_mixer->preparePlayback(m_OrgStreamID, m_PlaybackMixerChannel, /*active*/true, /*start_imm*/false); + + if (m_OrgStreamID.isValid()) { + sendStartPlayback(m_OrgStreamID); + sendPlaybackVolume(m_OrgStreamID, oldVolume); + } + + return true; +} + + +void TimeShifter::setTempFile(const QString &filename, Q_UINT64 s) +{ + m_RingBuffer.clear(); + m_RingBuffer.resize(m_TempFileName = filename, m_TempFileMaxSize = s); + m_PlaybackMetaData = SoundMetaData(0,0,0, i18n("internal stream, not stored")); + m_PlaybackDataLeftInBuffer = 0; +} + +bool TimeShifter::getSoundStreamDescription(SoundStreamID id, QString &descr) const +{ + if (id == m_NewStreamID) { + descr = name(); + return true; + } + else { + return false; + } +} + +bool TimeShifter::startCaptureWithFormat( + SoundStreamID id, + const SoundFormat &proposed_format, + SoundFormat &real_format, + bool force_format +) +{ + if (id == m_OrgStreamID) { + if (force_format && m_realSoundFormat != proposed_format) { + sendStopCapture(m_NewStreamID); + sendStartCaptureWithFormat(m_NewStreamID, proposed_format, m_realSoundFormat); + } + real_format = m_realSoundFormat; + return true; + } else { + return false; + } +} + +bool TimeShifter::stopCapture(SoundStreamID id) +{ + if (id == m_OrgStreamID) { + return true; + } else { + return false; + } +} + +#include "timeshifter.moc" diff --git a/kradio3/plugins/timeshifter/timeshifter.h b/kradio3/plugins/timeshifter/timeshifter.h new file mode 100644 index 0000000..32c3837 --- /dev/null +++ b/kradio3/plugins/timeshifter/timeshifter.h @@ -0,0 +1,120 @@ +/*************************************************************************** + timeshifter.h - description + ------------------- + begin : May 16 2005 + copyright : (C) 2005 Ernst Martin Witte + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef KRADIO_TIMESHIFTER_H +#define KRADIO_TIMESHIFTER_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "../../src/include/plugins.h" +#include "../../src/include/soundstreamclient_interfaces.h" +#include "../../src/include/fileringbuffer.h" + + +class TimeShifter : public QObject, + public PluginBase, + public ISoundStreamClient +{ +Q_OBJECT +public: + TimeShifter (const QString &name); + virtual ~TimeShifter (); + + virtual bool connectI (Interface *); + virtual bool disconnectI (Interface *); + + virtual QString pluginClassName() const { return "TimeShifter"; } + + virtual const QString &name() const { return PluginBase::name(); } + virtual QString &name() { return PluginBase::name(); } + + // config + + const QString &getPlaybackMixer() const { return m_PlaybackMixerID; } + const QString &getPlaybackMixerChannel() const { return m_PlaybackMixerChannel; } + const QString &getTempFileName() const { return m_TempFileName; } + Q_UINT64 getTempFileMaxSize() const { return m_TempFileMaxSize; } + + void setTempFile(const QString &filename, Q_UINT64 s); + bool setPlaybackMixer(const QString &soundStreamClientID, const QString &ch); + + // PluginBase + +public: + virtual void saveState (KConfig *) const; + virtual void restoreState (KConfig *); + + virtual ConfigPageInfo createConfigurationPage(); + virtual AboutPageInfo createAboutPage(); + +protected: + + ISoundStreamClient *searchPlaybackMixer(); + + size_t writeMetaDataToBuffer(const SoundMetaData &md, char *buffer, size_t buffer_size); + size_t readMetaDataFromBuffer(SoundMetaData &md, const char *buffer, size_t buffer_size); + void skipPacketInRingBuffer(); + + // SoundStreamClient + void noticeConnectedI (ISoundStreamServer *s, bool pointer_valid); + + bool startCaptureWithFormat(SoundStreamID id, + const SoundFormat &proposed_format, + SoundFormat &real_format, + bool force_format); + bool stopCapture(SoundStreamID id); + bool noticeSoundStreamClosed(SoundStreamID id); + bool startPlayback(SoundStreamID id); + bool stopPlayback(SoundStreamID id); + bool pausePlayback(SoundStreamID id); + bool noticeSoundStreamData(SoundStreamID id, const SoundFormat &sf, const char *data, size_t size, size_t &consumed_size, const SoundMetaData &md); + bool noticeReadyForPlaybackData(SoundStreamID id, size_t size); + + bool getSoundStreamDescription(SoundStreamID id, QString &descr) const; + + // FIXME: react on redirect request + +signals: + + void sigUpdateConfig(); + +protected: + + QString m_TempFileName; + size_t m_TempFileMaxSize; + SoundFormat m_SoundFormat; + SoundFormat m_realSoundFormat; + + QString m_PlaybackMixerID; + QString m_PlaybackMixerChannel; + + QString m_StreamFile; + bool m_StreamPaused; + SoundStreamID m_OrgStreamID; + SoundStreamID m_NewStreamID; + SoundFormat m_RealSoundFormat; + float m_orgVolume; + + SoundMetaData m_PlaybackMetaData; + size_t m_PlaybackDataLeftInBuffer; + + FileRingBuffer m_RingBuffer; +}; + +#endif |