diff --git a/src/abstractmediawidget.h b/src/abstractmediawidget.h index a1776e7..86fdb96 100644 --- a/src/abstractmediawidget.h +++ b/src/abstractmediawidget.h @@ -1,179 +1,179 @@ /* * abstractmediawidget.h * * Copyright (C) 2010-2012 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef ABSTRACTMEDIAWIDGET_H #define ABSTRACTMEDIAWIDGET_H #include #include "mediawidget.h" class AbstractMediaWidget : public QWidget { public: explicit AbstractMediaWidget(QWidget *parent); virtual ~AbstractMediaWidget() {}; void connectToMediaWidget(MediaWidget *mediaWidget_); // zero-based numbering is used everywhere (e.g. first audio channel = 0) MediaWidget::PlaybackStatus getPlaybackStatus() const { return playbackStatus; } int getCurrentTime() const { return currentTime; } // milliseconds int getTotalTime() const { return totalTime; } // milliseconds bool isSeekable() const { return seekable; } QMap getMetadata() const { return metadata; } QStringList getAudioStreams() const { return audioStreams; } int getCurrentAudioStream() const { return currentAudioStream; } QStringList getSubtitles() const { return subtitles; } int getCurrentSubtitle() const { return currentSubtitle; } int getTitleCount() const { return titleCount; } int getCurrentTitle() const { return currentTitle; } int getChapterCount() const { return chapterCount; } int getCurrentChapter() const { return currentChapter; } int getAngleCount() const { return angleCount; } int getCurrentAngle() const { return currentAngle; } bool hasDvdMenu() const { return dvdMenu; } QSize getVideoSize() const { return videoSize; } virtual QStringList getAudioDevices() = 0; virtual void setAudioDevice(QString device) = 0; virtual void setMuted(bool muted) = 0; virtual void setVolume(int volume) = 0; // [0 - 200] virtual void setAspectRatio(MediaWidget::AspectRatio aspectRatio) = 0; virtual void resizeToVideo(float scale) = 0; virtual void setDeinterlacing(MediaWidget::DeinterlaceMode) = 0; virtual void play(const MediaSource &source) = 0; virtual void stop() = 0; virtual void setPaused(bool paused) = 0; virtual void seek(int time) = 0; // milliseconds virtual void setCurrentAudioStream(int currentAudioStream) = 0; virtual void setCurrentSubtitle(int currentSubtitle) = 0; virtual void setExternalSubtitle(const QUrl &subtitleUrl) = 0; virtual void setCurrentTitle(int currentTitle) = 0; virtual void setCurrentChapter(int currentChapter) = 0; virtual void setCurrentAngle(int currentAngle) = 0; virtual bool jumpToPreviousChapter() = 0; virtual bool jumpToNextChapter() = 0; virtual void showDvdMenu() = 0; virtual void dvdNavigate(int key) = 0; enum PendingUpdate { PlaybackFinished = (1 << 0), PlaybackStatus = (1 << 1), CurrentTotalTime = (1 << 2), Seekable = (1 << 3), Metadata = (1 << 4), AudioStreams = (1 << 5), Subtitles = (1 << 6), Titles = (1 << 7), Chapters = (1 << 8), Angles = (1 << 9), DvdMenu = (1 << 10), VideoSize = (1 << 11) }; Q_DECLARE_FLAGS(PendingUpdates, PendingUpdate) protected: void addPendingUpdates(PendingUpdates pendingUpdatesToBeAdded); // thread-safe virtual int updatePlaybackStatus() = 0; virtual void updateCurrentTotalTime() = 0; virtual void updateSeekable() = 0; virtual void updateMetadata() = 0; virtual void updateAudioStreams() = 0; virtual void updateSubtitles() = 0; virtual void updateTitles() = 0; virtual void updateChapters() = 0; virtual void updateAngles() = 0; virtual void updateDvdMenu() = 0; virtual void updateVideoSize() = 0; MediaWidget::PlaybackStatus playbackStatus; int currentTime; int totalTime; bool seekable; QMap metadata; QStringList audioStreams; int currentAudioStream; QStringList subtitles; int currentSubtitle; int titleCount; int currentTitle; int chapterCount; int currentChapter; int angleCount; int currentAngle; bool dvdMenu; QSize videoSize; private: - void customEvent(QEvent *event); + void customEvent(QEvent *event) override; MediaWidget *mediaWidget; QAtomicInt pendingUpdates; }; Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMediaWidget::PendingUpdates) class DummyMediaWidget : public AbstractMediaWidget { public: explicit DummyMediaWidget(QWidget *parent): AbstractMediaWidget(parent) {}; ~DummyMediaWidget() {}; - QStringList getAudioDevices() { QStringList empty; return empty; }; - void setAudioDevice(QString) {}; - void setMuted(bool) {}; - void setVolume(int) {}; // [0 - 200] - void setAspectRatio(MediaWidget::AspectRatio) {}; - void resizeToVideo(float) {}; - void setDeinterlacing(MediaWidget::DeinterlaceMode) {}; - void play(const MediaSource &) {}; - void stop() {}; - void setPaused(bool) {}; - void seek(int) {}; // milliseconds - void setCurrentAudioStream(int) {}; - void setCurrentSubtitle(int) {}; - void setExternalSubtitle(const QUrl &) {}; - void setCurrentTitle(int) {}; - void setCurrentChapter(int) {}; - void setCurrentAngle(int) {}; - bool jumpToPreviousChapter() { return false; }; - bool jumpToNextChapter() { return false; } - void showDvdMenu() {}; - void dvdNavigate(int) {}; - - int updatePlaybackStatus() { return true; }; - void updateCurrentTotalTime() {}; - void updateSeekable() {}; - void updateMetadata() {}; + QStringList getAudioDevices() override { QStringList empty; return empty; }; + void setAudioDevice(QString) override {}; + void setMuted(bool) override {}; + void setVolume(int) override {}; // [0 - 200] + void setAspectRatio(MediaWidget::AspectRatio) override {}; + void resizeToVideo(float) override {}; + void setDeinterlacing(MediaWidget::DeinterlaceMode) override {}; + void play(const MediaSource &) override {}; + void stop() override {}; + void setPaused(bool) override {}; + void seek(int) override {}; // milliseconds + void setCurrentAudioStream(int) override {}; + void setCurrentSubtitle(int) override {}; + void setExternalSubtitle(const QUrl &) override {}; + void setCurrentTitle(int) override {}; + void setCurrentChapter(int) override {}; + void setCurrentAngle(int) override {}; + bool jumpToPreviousChapter() override { return false; }; + bool jumpToNextChapter() override { return false; } + void showDvdMenu() override {}; + void dvdNavigate(int) override {}; + + int updatePlaybackStatus() override { return true; }; + void updateCurrentTotalTime() override {}; + void updateSeekable() override {}; + void updateMetadata() override {}; void updateAudioDevices() {}; - void updateAudioStreams() {}; - void updateSubtitles() {}; - void updateTitles() {}; - void updateChapters() {}; - void updateAngles() {}; - void updateDvdMenu() {}; - void updateVideoSize() {}; + void updateAudioStreams() override {}; + void updateSubtitles() override {}; + void updateTitles() override {}; + void updateChapters() override {}; + void updateAngles() override {}; + void updateDvdMenu() override {}; + void updateVideoSize() override {}; }; #endif /* ABSTRACTMEDIAWIDGET_H */ diff --git a/src/backend-vlc/vlcmediawidget.h b/src/backend-vlc/vlcmediawidget.h index 38e09d9..63aa1a5 100644 --- a/src/backend-vlc/vlcmediawidget.h +++ b/src/backend-vlc/vlcmediawidget.h @@ -1,107 +1,107 @@ /* * vlcmediawidget.h * * Copyright (C) 2010-2012 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef VLCMEDIAWIDGET_H #define VLCMEDIAWIDGET_H #include #include "../abstractmediawidget.h" class QTimer; class VlcMediaWidget : public AbstractMediaWidget { Q_OBJECT private: explicit VlcMediaWidget(QWidget *parent); bool init(); private slots: void hideMouse(); public: ~VlcMediaWidget(); static VlcMediaWidget *createVlcMediaWidget(QWidget *parent); // returns NULL if init fails // zero-based numbering is used everywhere (e.g. first audio channel = 0) - QStringList getAudioDevices(); - void setAudioDevice(QString device); - void setMuted(bool muted); - void setVolume(int volume); // [0 - 200] - void setAspectRatio(MediaWidget::AspectRatio aspectRatio); - void resizeToVideo(float resizeFactor); - void setDeinterlacing(MediaWidget::DeinterlaceMode deinterlacing); - void play(const MediaSource &source); - void stop(); - void setPaused(bool paused); - void seek(int time); // milliseconds - void setCurrentAudioStream(int currentAudioStream); - void setCurrentSubtitle(int currentSubtitle); - void setExternalSubtitle(const QUrl &subtitleUrl); - void setCurrentTitle(int currentTitle); - void setCurrentChapter(int currentChapter); - void setCurrentAngle(int currentAngle); - bool jumpToPreviousChapter(); - bool jumpToNextChapter(); - void showDvdMenu(); - void dvdNavigate(int key); + QStringList getAudioDevices() override; + void setAudioDevice(QString device) override; + void setMuted(bool muted) override; + void setVolume(int volume) override; // [0 - 200] + void setAspectRatio(MediaWidget::AspectRatio aspectRatio) override; + void resizeToVideo(float resizeFactor) override; + void setDeinterlacing(MediaWidget::DeinterlaceMode deinterlacing) override; + void play(const MediaSource &source) override; + void stop() override; + void setPaused(bool paused) override; + void seek(int time) override; // milliseconds + void setCurrentAudioStream(int currentAudioStream) override; + void setCurrentSubtitle(int currentSubtitle) override; + void setExternalSubtitle(const QUrl &subtitleUrl) override; + void setCurrentTitle(int currentTitle) override; + void setCurrentChapter(int currentChapter) override; + void setCurrentAngle(int currentAngle) override; + bool jumpToPreviousChapter() override; + bool jumpToNextChapter() override; + void showDvdMenu() override; + void dvdNavigate(int key) override; void playDirection(int direction); int makePlay(); - int updatePlaybackStatus(); - void updateCurrentTotalTime(); - void updateSeekable(); - void updateMetadata(); - void updateAudioStreams(); - void updateSubtitles(); - void updateTitles(); - void updateChapters(); - void updateAngles(); - void updateDvdMenu(); - void updateVideoSize(); + int updatePlaybackStatus() override; + void updateCurrentTotalTime() override; + void updateSeekable() override; + void updateMetadata() override; + void updateAudioStreams() override; + void updateSubtitles() override; + void updateTitles() override; + void updateChapters() override; + void updateAngles() override; + void updateDvdMenu() override; + void updateVideoSize() override; void unregisterEvents(); bool registerEvents(); private: - void mousePressEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; void vlcEvent(const libvlc_event_t *event); static void vlcEventHandler(const libvlc_event_t *event, void *instance); QTimer *timer; libvlc_instance_t *vlcInstance; libvlc_media_t *vlcMedia; libvlc_media_player_t *vlcMediaPlayer; libvlc_event_manager_t *eventManager; bool isPaused; bool playingDvd; bool urlIsAudioCd; QMap subtitleId; QByteArray typeOfDevice; int trackNumber, numTracks; QVector eventType; }; #endif /* VLCMEDIAWIDGET_H */ diff --git a/src/configurationdialog.h b/src/configurationdialog.h index 66c25a2..e5432d9 100644 --- a/src/configurationdialog.h +++ b/src/configurationdialog.h @@ -1,68 +1,68 @@ /* * configurationdialog.h * * Copyright (C) 2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef CONFIGURATIONDIALOG_H #define CONFIGURATIONDIALOG_H #include class QSpinBox; class QString; class QComboBox; class QLineEdit; class Log { public: Log() {}; void storeLog(QString &newlog) { log.append(newlog); } QString getLog() { return log; } private: static QString log; }; class ConfigurationDialog : public KPageDialog { Q_OBJECT public: explicit ConfigurationDialog(QWidget *parent); ~ConfigurationDialog(); - void accept(); + void accept() override; private slots: void showDmesg(); private: QComboBox *startupDisplayModeBox; QSpinBox *shortSkipBox; QSpinBox *longSkipBox; QLineEdit *libVlcArguments; }; #endif /* CONFIGURATIONDIALOG_H */ diff --git a/src/dvb/dvbcam_linux.h b/src/dvb/dvbcam_linux.h index 818c6b9..19b6a39 100644 --- a/src/dvb/dvbcam_linux.h +++ b/src/dvb/dvbcam_linux.h @@ -1,146 +1,146 @@ /* * dvbcam_linux.h * * Copyright (C) 2010-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DVBCAM_LINUX_H #define DVBCAM_LINUX_H #include #include class QSocketNotifier; class DvbLinuxCamService; class DvbPmtSection; class DvbLinuxCam : public QObject { Q_OBJECT public: DvbLinuxCam(QObject *parent); ~DvbLinuxCam(); void startCa(const QString &path); void startDescrambling(const QByteArray &pmtSection); void stopDescrambling(int serviceId); void stopCa(); private slots: void pollModule(); void readyRead(); public: enum PendingCommand { ExpectingReply = (1 << 0), ResetCa = (1 << 1), SendCreateTransportConnection = (1 << 2), SendReceiveData = (1 << 3), SendPoll = (1 << 4), SendProfileEnquiry = (1 << 5), SendProfileChange = (1 << 6), SendApplicationInfoEnquiry = (1 << 7), SendCaInfoEnquiry = (1 << 8) }; Q_DECLARE_FLAGS(PendingCommands, PendingCommand) private: Q_DISABLE_COPY(DvbLinuxCam) enum TransportLayerTag { StatusByte = 0x80, ReceiveData = 0x81, CreateTransportConnection = 0x82, CreateTransportConnectionReply = 0x83, DataLast = 0xa0 }; enum { ConnectionId = 0x01, HeaderSize = 17 }; enum SessionLayerTag { OpenSessionRequest = 0x91, OpenSessionResponse = 0x92, SessionNumber = 0x90 }; enum Resources { ResourceManager = 0x00010041, ApplicationInformation = 0x00020041, ConditionalAccess = 0x00030041 }; enum SessionNumbers { ResourceManagerSession = 1, ApplicationInformationSession = 2, ConditionalAccessSession = 3 }; enum ApplicationLayerTag { ProfileEnquiry = 0x9f8010, ProfileReply = 0x9f8011, ProfileChange = 0x9f8012, ApplicationInfoEnquiry = 0x9f8020, ApplicationInfo = 0x9f8021, CaInfoEnquiry = 0x9f8030, CaInfo = 0x9f8031, CaPmt = 0x9f8032 }; enum CaPmtListManagement { Only = 0x03, Add = 0x04, Update = 0x05 }; enum CaPmtCommand { Descramble = 0x01, StopDescrambling = 0x04 }; bool detectSlot(); int decodeLength(const unsigned char *&data, int &size); void resize(int messageSize); void handleTransportLayer(const unsigned char *data, int size); void handleSessionLayer(const unsigned char *data, int size); void handleApplicationLayer(const unsigned char *data, int size); void handlePendingCommands(); - void customEvent(QEvent *event); + void customEvent(QEvent *event) override; void sendCaPmt(const DvbPmtSection &pmtSection, CaPmtListManagement listManagement, CaPmtCommand command); void sendApplicationLayerMessage(ApplicationLayerTag tag, char *data, char *end); void sendSessionLayerMessage(SessionLayerTag tag, char *data, char *end); void sendTransportLayerMessage(TransportLayerTag tag, char *data, char *end); int caFd; int slot; QTimer pollTimer; QSocketNotifier *socketNotifier; PendingCommands pendingCommands; QByteArray message; char *messageData; bool ready; bool eventPosted; QMap services; }; Q_DECLARE_OPERATORS_FOR_FLAGS(DvbLinuxCam::PendingCommands) #endif /* DVBCAM_LINUX_H */ diff --git a/src/dvb/dvbchannel.h b/src/dvb/dvbchannel.h index 37ce647..0e718b9 100644 --- a/src/dvb/dvbchannel.h +++ b/src/dvb/dvbchannel.h @@ -1,136 +1,136 @@ /* * dvbchannel.h * * Copyright (C) 2007-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DVBCHANNEL_H #define DVBCHANNEL_H #include #include #include "../shareddata.h" #include "../sqlinterface.h" #include "dvbtransponder.h" class DvbChannel : public SharedData, public SqlKey { public: DvbChannel() : number(-1), networkId(-1), transportStreamId(-1), pmtPid(-1), serviceId(-1), audioPid(-1), hasVideo(false), isScrambled(false) { } ~DvbChannel() { } // checks that all variables are ok // updates 'pmtSectionData' if 'serviceId' is valid // (else updates 'serviceId' if 'pmtSectionData' is valid) // 'sqlKey' is ignored bool validate(); QString name; int number; QString source; DvbTransponder transponder; int networkId; // may be -1 (not present), atsc meaning: source id int transportStreamId; int pmtPid; QByteArray pmtSectionData; int serviceId; int audioPid; // may be -1 (not present), doesn't have to be present in the pmt section bool hasVideo; bool isScrambled; }; typedef ExplicitlySharedDataPointer DvbSharedChannel; Q_DECLARE_TYPEINFO(DvbSharedChannel, Q_MOVABLE_TYPE); class DvbChannelId { public: explicit DvbChannelId(const DvbChannel *channel_) : channel(channel_) { } explicit DvbChannelId(const DvbSharedChannel &channel_) : channel(channel_.constData()) { } ~DvbChannelId() { } // compares channels by transmission type and // ( dvb) 'source', 'networkId', 'transportStreamId', 'serviceId' // (atsc) 'source', 'transponder', 'networkId' bool operator==(const DvbChannelId &other) const; bool operator!=(const DvbChannelId &other) const { return !(*this == other); } friend uint qHash(const DvbChannelId &channel); private: const DvbChannel *channel; }; class DvbChannelModel : public QObject, private SqlInterface { Q_OBJECT public: explicit DvbChannelModel(QObject *parent); ~DvbChannelModel(); static DvbChannelModel *createSqlModel(QObject *parent); // channel names and numbers are guaranteed to be unique within this model QMap getChannels() const; DvbSharedChannel findChannelByName(const QString &channelName) const; bool hasChannelByName(const QString &channelName); DvbSharedChannel findChannelByNumber(int channelNumber) const; DvbSharedChannel findChannelById(const DvbChannel &channel) const; void cloneFrom(DvbChannelModel *other); void addChannel(DvbChannel &channel); void updateChannel(DvbSharedChannel channel, DvbChannel &modifiedChannel); void removeChannel(DvbSharedChannel channel); bool areInTheSameBunch(DvbSharedChannel channel1, DvbSharedChannel channel2); void dndMoveChannels(const QList &selectedChannels, int insertBeforeNumber); void channelFlush(); signals: void channelAdded(const DvbSharedChannel &channel); // if this is the main model, updating doesn't change the channel pointer // (modifies existing content); otherwise the channel pointer may be updated void channelAboutToBeUpdated(const DvbSharedChannel &channel); void channelUpdated(const DvbSharedChannel &channel); void channelRemoved(const DvbSharedChannel &channel); private: - void bindToSqlQuery(SqlKey sqlKey, QSqlQuery &query, int index) const; - bool insertFromSqlQuery(SqlKey sqlKey, const QSqlQuery &query, int index); + void bindToSqlQuery(SqlKey sqlKey, QSqlQuery &query, int index) const override; + bool insertFromSqlQuery(SqlKey sqlKey, const QSqlQuery &query, int index) override; QString extractBaseName(const QString &name) const; QString findNextFreeChannelName(const QString &name) const; int findNextFreeChannelNumber(int number) const; QMap channelNames; QMap channelNumbers; QMultiHash channelIds; QMap channels; // only used for the sql model bool hasPendingOperation; bool isSqlModel; }; #endif /* DVBCHANNEL_H */ diff --git a/src/dvb/dvbchanneldialog.h b/src/dvb/dvbchanneldialog.h index 4687a01..8a716dc 100644 --- a/src/dvb/dvbchanneldialog.h +++ b/src/dvb/dvbchanneldialog.h @@ -1,162 +1,162 @@ /* * dvbchanneldialog.h * * Copyright (C) 2007-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DVBCHANNELDIALOG_H #define DVBCHANNELDIALOG_H #include #include "../tablemodel.h" #include "dvbchannel.h" class QAction; class DvbChannelLessThan { public: DvbChannelLessThan() : sortOrder(ChannelNameAscending) { } ~DvbChannelLessThan() { } enum SortOrder { ChannelNameAscending, ChannelNameDescending, ChannelNumberAscending, ChannelNumberDescending }; SortOrder getSortOrder() const { return sortOrder; } void setSortOrder(SortOrder sortOrder_) { sortOrder = sortOrder_; } bool operator()(const DvbSharedChannel &x, const DvbSharedChannel &y) const; private: SortOrder sortOrder; }; class DvbChannelTableModelHelper { public: DvbChannelTableModelHelper() { } ~DvbChannelTableModelHelper() { } typedef DvbSharedChannel ItemType; typedef DvbChannelLessThan LessThanType; int columnCount() const { return 2; } bool filterAcceptsItem(const DvbSharedChannel &channel) const { // Qt::CaseSensitive == no filtering return ((filter.caseSensitivity() == Qt::CaseSensitive) || (filter.indexIn(channel->name) >= 0)); } QStringMatcher filter; private: Q_DISABLE_COPY(DvbChannelTableModelHelper) }; class DvbChannelTableModel : public TableModel { Q_OBJECT public: explicit DvbChannelTableModel(QObject *parent); ~DvbChannelTableModel(); void setChannelModel(DvbChannelModel *channelModel_); DvbChannelModel *getChannelModel() const { return channelModel; } - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - QVariant data(const QModelIndex &index, int role) const; - void sort(int column, Qt::SortOrder order); - - Qt::ItemFlags flags(const QModelIndex &index) const; - QMimeData *mimeData(const QModelIndexList &indexes) const; - QStringList mimeTypes() const; - Qt::DropActions supportedDropActions() const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + QVariant data(const QModelIndex &index, int role) const override; + void sort(int column, Qt::SortOrder order) override; + + Qt::ItemFlags flags(const QModelIndex &index) const override; + QMimeData *mimeData(const QModelIndexList &indexes) const override; + QStringList mimeTypes() const override; + Qt::DropActions supportedDropActions() const override; bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, - const QModelIndex &parent); + const QModelIndex &parent) override; public slots: void setFilter(const QString &filter); signals: void checkChannelDragAndDrop(bool *ok); private slots: void channelAdded(const DvbSharedChannel &channel); void channelAboutToBeUpdated(const DvbSharedChannel &channel); void channelUpdated(const DvbSharedChannel &channel); void channelRemoved(const DvbSharedChannel &channel); private: - void customEvent(QEvent *event); + void customEvent(QEvent *event) override; DvbChannelModel *channelModel; QList dndSelectedChannels; int dndInsertBeforeNumber; bool dndEventPosted; }; class DvbChannelView : public QTreeView { Q_OBJECT public: explicit DvbChannelView(QWidget *parent); ~DvbChannelView(); QAction *addEditAction(); QAction *addRemoveAction(); void setModel(DvbChannelTableModel *tableModel_); public slots: void checkChannelDragAndDrop(bool *ok); void editChannel(); void removeChannel(); void removeAllChannels(); signals: void channelPidsUpdated(const DvbSharedChannel &channel); private slots: void channelPidsChanged(const DvbSharedChannel &channel); private: - void setModel(QAbstractItemModel *) { } + void setModel(QAbstractItemModel *) override { } DvbChannelTableModel *tableModel; }; #endif /* DVBCHANNELDIALOG_H */ diff --git a/src/dvb/dvbchanneldialog_p.h b/src/dvb/dvbchanneldialog_p.h index 803d13e..8335282 100644 --- a/src/dvb/dvbchanneldialog_p.h +++ b/src/dvb/dvbchanneldialog_p.h @@ -1,61 +1,61 @@ /* * dvbchanneldialog_p.h * * Copyright (C) 2007-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DVBCHANNELDIALOG_P_H #define DVBCHANNELDIALOG_P_H #include #include "dvbchannel.h" class QCheckBox; class QSpinBox; class QComboBox; class QLineEdit; class DvbChannelTableModel; Q_DECLARE_METATYPE(QList) class DvbChannelEditor : public QDialog { Q_OBJECT public: DvbChannelEditor(DvbChannelTableModel *model_, const DvbSharedChannel &channel_, QWidget *parent); ~DvbChannelEditor(); private: - void accept(); + void accept() override; DvbChannelTableModel *model; DvbSharedChannel channel; QLineEdit *nameEdit; QSpinBox *numberBox; QSpinBox *networkIdBox; QSpinBox *transportStreamIdBox; QSpinBox *serviceIdBox; QComboBox *audioStreamBox; QList audioPids; QCheckBox *scrambledBox; signals: void channelPidsChanged(const DvbSharedChannel &channel); }; #endif /* DVBCHANNELDIALOG_P_H */ diff --git a/src/dvb/dvbconfigdialog.h b/src/dvb/dvbconfigdialog.h index f46de8d..476afec 100644 --- a/src/dvb/dvbconfigdialog.h +++ b/src/dvb/dvbconfigdialog.h @@ -1,291 +1,291 @@ /* * dvbconfigdialog.h * * Copyright (C) 2007-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DVBCONFIGDIALOG_H #define DVBCONFIGDIALOG_H #include #include #include #include class QBoxLayout; class QButtonGroup; class QCheckBox; class QGridLayout; class QLabel; class QProgressBar; class QSpinBox; class QTreeWidget; class QComboBox; class QComboBox; class KJob; class QLineEdit; class QTabWidget; class QDialogButtonBox; namespace KIO { class Job; class TransferJob; } class DvbConfig; class DvbConfigBase; class DvbConfigPage; class DvbDeviceConfig; class DvbManager; class DvbDevice; class DvbSConfigObject; class DvbSLnbConfigObject; class RegexInputLine : public QObject { Q_OBJECT public: int index; QLineEdit *lineEdit; QSpinBox *spinBox; QCheckBox *checkBox; }; class DvbConfigDialog : public QDialog { Q_OBJECT public: DvbConfigDialog(DvbManager *manager_, QWidget *parent); ~DvbConfigDialog(); signals: void removeRegex(DvbConfigPage *page); private slots: void changeRecordingFolder(); void changeTimeShiftFolder(); void changeXmltvFileName(); void updateScanFile(); void openScanFile(); void newRegex(); void removeRegex(); void namingFormatChanged(QString text); void moveLeft(DvbConfigPage *configPage); void moveRight(DvbConfigPage *configPage); void remove(DvbConfigPage *configPage); private: void removeWidgets(QGridLayout *layout, int row, int column, bool deleteWidgets); void initRegexButtons(QGridLayout *buttonGrid); //void deleteChildWidgets(QLayoutItem *item); - void accept(); + void accept() override; DvbManager *manager; QTabWidget *tabWidget; QLineEdit *recordingFolderEdit; QLineEdit *timeShiftFolderEdit; QLineEdit *xmltvFileNameEdit; QSpinBox *beginMarginBox; QSpinBox *endMarginBox; QLineEdit *namingFormat; QCheckBox *override6937CharsetBox; QCheckBox *createInfoFileBox; QCheckBox *disableEpgBox; QCheckBox *scanWhenIdleBox; QPixmap validPixmap; QPixmap invalidPixmap; QLabel *namingFormatValidLabel; QList configPages; QLineEdit *actionAfterRecordingLineEdit; QGridLayout *regexGrid; QList regexInputList; }; class DvbScanFileDownloadDialog : public QDialog { Q_OBJECT public: DvbScanFileDownloadDialog(DvbManager *manager_, QWidget *parent); ~DvbScanFileDownloadDialog(); private slots: void progressChanged(KJob *, unsigned long percent); void dataArrived(KIO::Job *, const QByteArray &data); void jobFinished(); private: DvbManager *manager; QProgressBar *progressBar; QLabel *label; KIO::TransferJob *job; QByteArray scanData; QVBoxLayout *mainLayout; QDialogButtonBox *buttonBox; }; class DvbConfigPage : public QWidget { Q_OBJECT public: DvbConfigPage(QWidget *parent, DvbManager *manager, const DvbDeviceConfig *deviceConfig_); ~DvbConfigPage(); void setMoveLeftEnabled(bool enabled); void setMoveRightEnabled(bool enabled); const DvbDeviceConfig *getDeviceConfig() const; QList getConfigs(); signals: void moveLeft(DvbConfigPage *page); void moveRight(DvbConfigPage *page); void remove(DvbConfigPage *page); void resetConfig(); private slots: void moveLeft(); void moveRight(); void removeConfig(); private: void addHSeparator(const QString &title); const DvbDeviceConfig *deviceConfig; QBoxLayout *boxLayout; QPushButton *moveLeftButton; QPushButton *moveRightButton; QList configs; DvbSConfigObject *dvbSObject; }; class DvbConfigObject : public QObject { Q_OBJECT public: DvbConfigObject(QWidget *parent, QBoxLayout *layout, DvbManager *manager, DvbConfigBase *config_, bool isGen2); ~DvbConfigObject(); private slots: void timeoutChanged(int timeout); void sourceChanged(int index); void nameChanged(); void resetConfig(); private: DvbConfigBase *config; QString defaultName; QSpinBox *timeoutBox; QComboBox *sourceBox; QLineEdit *nameEdit; bool isGen2; }; class DvbSConfigObject : public QObject { Q_OBJECT public: DvbSConfigObject(QWidget *parent_, QBoxLayout *boxLayout, DvbManager *manager, const QList &configs, DvbDevice *device, bool isGen2); ~DvbSConfigObject(); void appendConfigs(QList &list); signals: void setDiseqcVisible(bool visible); void setFirstLnbVisible(bool visible); void setRotorVisible(bool visible); // common parts of usals / positions ui void setUsalsVisible(bool visible); // usals-specific parts of ui void setPositionsVisible(bool visible); // positions-specific parts of ui private slots: void latitudeChanged(const QString &text); void longitudeChanged(const QString &text); void configChanged(int index); void addSatellite(); void removeSatellite(); void resetConfig(); private: DvbConfigBase *createConfig(int lnbNumber); static double toLatitude(const QString &text, bool *ok); static double toLongitude(const QString &text, bool *ok); QWidget *parent; DvbDevice *device; DvbConfigBase *lnbConfig; QList diseqcConfigs; QList lnbConfigs; QStringList sources; QGridLayout *layout; QSpinBox *timeoutBox; QCheckBox *higherVoltageBox; QComboBox *configBox; QComboBox *sourceBox; QSpinBox *rotorSpinBox; QTreeWidget *satelliteView; QPixmap validPixmap; QPixmap invalidPixmap; QLabel *latitudeValidLabel; QLabel *longitudeValidLabel; QLineEdit *latitudeEdit; QLineEdit *longitudeEdit; }; class DvbSLnbConfigObject : public QObject { Q_OBJECT public: DvbSLnbConfigObject(QSpinBox *timeoutSpinBox, QCheckBox *higherVoltageBox, QComboBox *sourceBox_, QPushButton *configureButton_, DvbConfigBase *config_, DvbDevice *device_); ~DvbSLnbConfigObject(); void resetConfig(); private slots: void timeoutChanged(int value); void higherVoltageChanged(int value); void sourceChanged(int index); void configure(); void selectType(int type); void dialogAccepted(); private: QComboBox *sourceBox; QPushButton *configureButton; DvbConfigBase *config; DvbDevice *device; QDialog *dialog; QButtonGroup *lnbSelectionGroup; QLabel *lowBandLabel; QLabel *switchLabel; QLabel *highBandLabel; QLabel *lowRangeLabel; QLabel *highRangeLabel; QSpinBox *lowBandSpinBox; QSpinBox *switchSpinBox; QSpinBox *highBandSpinBox; int currentType; }; #endif /* DVBCONFIGDIALOG_H */ diff --git a/src/dvb/dvbdevice.cpp b/src/dvb/dvbdevice.cpp index 026a28a..30ba70e 100644 --- a/src/dvb/dvbdevice.cpp +++ b/src/dvb/dvbdevice.cpp @@ -1,1003 +1,1003 @@ /* * dvbdevice.cpp * * Copyright (C) 2007-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "../log.h" #include #include #include #include #include "dvbconfig.h" #include "dvbdevice.h" #include "dvbdevice_p.h" #include "dvbmanager.h" #include "dvbsi.h" class DvbFilterInternal { public: DvbFilterInternal() : activeFilters(0) { } ~DvbFilterInternal() { } QList filters; int activeFilters; }; class DvbSectionFilterInternal : public DvbPidFilter { public: DvbSectionFilterInternal() : activeSectionFilters(0), continuityCounter(0), wrongCrcIndex(0), bufferValid(false) { memset(wrongCrcs, 0, sizeof(wrongCrcs)); } ~DvbSectionFilterInternal() { } QList sectionFilters; int activeSectionFilters; private: - void processData(const char [188]); + void processData(const char [188]) override; void processSections(bool force); unsigned char continuityCounter; unsigned char wrongCrcIndex; bool bufferValid; QByteArray buffer; int wrongCrcs[8]; }; // FIXME some debug messages may be printed too often void DvbSectionFilterInternal::processData(const char data[188]) { if ((data[3] & 0x10) == 0) { // no payload qCDebug(logDvb, "MPEG-TS session doesn't have a payload"); return; } unsigned char continuity = (data[3] & 0x0f); if (bufferValid) { if (continuity == continuityCounter) { qCDebug(logDvb, "Section duplication: received %d, expecting %d", continuity, continuityCounter + 1); return; } if (continuity != ((continuityCounter + 1) & 0x0f)) { qCDebug(logDvb, "Section discontinuity: received %d, expecting %d", continuity, continuityCounter + 1); bufferValid = false; } } continuityCounter = continuity; bool sectionStart = ((data[1] & 0x40) != 0); const char *payload; int payloadLength; if ((data[3] & 0x20) == 0) { // adaptation field not present payload = (data + 4); payloadLength = (188 - 4); } else { // adaptation field present unsigned char length = data[4]; if (length > 182) { qCDebug(logDvb, "Received a section without payload or corrupted"); return; } payload = (data + 5 + length); payloadLength = (188 - 5 - length); } // be careful that playloadLength is > 0 at this point if (sectionStart) { int pointer = quint8(payload[0]); if (pointer >= payloadLength) { qCDebug(logDvb, "Section with invalid payload pointer"); pointer = (payloadLength - 1); } if (bufferValid) { buffer.append(payload + 1, pointer); processSections(true); } else { bufferValid = true; } payload += (pointer + 1); payloadLength -= (pointer + 1); } buffer.append(payload, payloadLength); processSections(false); } void DvbSectionFilterInternal::processSections(bool force) { const char *it = buffer.constBegin(); const char *end = buffer.constEnd(); while (it != end) { if (static_cast(it[0]) == 0xff) { // table id == 0xff means padding it = end; break; } if ((end - it) < 3) { if (force) { qCDebug(logDvb, "Section with stray data"); it = end; } break; } const char *sectionEnd = (it + (((static_cast(it[1]) & 0x0f) << 8) | static_cast(it[2])) + 3); if (force && (sectionEnd > end)) { qCDebug(logDvb, "Short section"); sectionEnd = end; } if (sectionEnd <= end) { int size = int(sectionEnd - it); int crc = DvbStandardSection::verifyCrc32(it, size); bool crcOk; if (crc == 0) { crcOk = true; } else { for (int i = 0;; ++i) { if (i == (sizeof(wrongCrcs) / sizeof(wrongCrcs[0]))) { crcOk = false; wrongCrcs[wrongCrcIndex] = crc; if ((++wrongCrcIndex) == i) { wrongCrcIndex = 0; } break; } if (wrongCrcs[i] == crc) { crcOk = true; break; } } } if (crcOk) { for (int i = 0; i < sectionFilters.size(); ++i) { sectionFilters.at(i)->processSection(it, size); } } it = sectionEnd; continue; } break; } buffer.remove(0, int(it - buffer.constBegin())); } class DvbDataDumper : public QFile, public DvbPidFilter { public: DvbDataDumper(); ~DvbDataDumper(); - void processData(const char [188]); + void processData(const char [188]) override; }; DvbDataDumper::DvbDataDumper() { setFileName(QDir::homePath() + QLatin1String("/KaffeineDvbDump-") + QString::number(qrand(), 16) + QLatin1String(".bin")); if (!open(QIODevice::WriteOnly | QIODevice::Truncate)) { qCWarning(logDev, "Can't open %s", qPrintable(fileName())); } } DvbDataDumper::~DvbDataDumper() { } void DvbDataDumper::processData(const char data[188]) { write(data, 188); } DvbDevice::DvbDevice(DvbBackendDevice *backend_, QObject *parent) : QObject(parent), backend(backend_), deviceState(DeviceReleased), dataDumper(NULL), cleanUpFilters(false), isAuto(false), unusedBuffersHead(NULL), usedBuffersHead(NULL), usedBuffersTail(NULL) { backend->setFrontendDevice(this); backend->setDeviceEnabled(true); // FIXME connect(&frontendTimer, SIGNAL(timeout()), this, SLOT(frontendEvent())); } DvbDevice::~DvbDevice() { backend->release(); for (DvbDeviceDataBuffer *buffer = unusedBuffersHead; buffer != NULL;) { DvbDeviceDataBuffer *nextBuffer = buffer->next; delete buffer; buffer = nextBuffer; } for (DvbDeviceDataBuffer *buffer = usedBuffersHead; buffer != NULL;) { DvbDeviceDataBuffer *nextBuffer = buffer->next; delete buffer; buffer = nextBuffer; } } DvbDevice::TransmissionTypes DvbDevice::getTransmissionTypes() const { return backend->getTransmissionTypes(); } QString DvbDevice::getDeviceId() const { return backend->getDeviceId(); } QString DvbDevice::getFrontendName() const { return backend->getFrontendName(); } void DvbDevice::tune(const DvbTransponder &transponder) { DvbTransponderBase::TransmissionType transmissionType = transponder.getTransmissionType(); autoTransponder.setTransmissionType(transmissionType); if ((transmissionType != DvbTransponderBase::DvbS) && (transmissionType != DvbTransponderBase::DvbS2)) { if (backend->tune(transponder)) { setDeviceState(DeviceTuning); frontendTimeout = config->timeout; frontendTimer.start(100); discardBuffers(); } else { setDeviceState(DeviceTuning); autoTransponder.setTransmissionType(DvbTransponderBase::Invalid); setDeviceState(DeviceIdle); } return; } bool moveRotor = false; DvbTransponder intermediate = transponder; // DVB LNBf IF and DiSeqC switch settings int satNumber = -1; // No DiseqC Switch if (config->configuration == DvbConfigBase::DiseqcSwitch) satNumber = config->lnbNumber; // FIXME: add support for SCR/Unicable if (!backend->satSetup(config->currentLnb.alias, satNumber, 0)) return; backend->setHighVoltage(config->higherVoltage); // rotor switch (config->configuration) { case DvbConfigBase::DiseqcSwitch: case DvbConfigBase::NoDiseqc: // Everything was already prepared via satSetup(). break; case DvbConfigBase::UsalsRotor: { QString source = config->scanSource; source.remove(0, source.lastIndexOf(QLatin1Char('-')) + 1); bool ok = false; double orbitalPosition = 0; if (source.endsWith(QLatin1Char('E'))) { source.chop(1); orbitalPosition = source.toDouble(&ok); } else if (source.endsWith(QLatin1Char('W'))) { source.chop(1); orbitalPosition = (-source.toDouble(&ok)); } if (!ok) qCWarning(logDev, "Can't extract orbital position from %s", qPrintable(config->scanSource)); double radius = 6378; double semiMajorAxis = 42164; double temp = (radius * cos(config->latitude * M_PI / 180)); double temp2 = ((orbitalPosition - config->longitude) * M_PI / 180); double angle = (temp2 + atan(sin(temp2) / ((semiMajorAxis / temp) - cos(temp2)))); int value = 0; if (angle >= 0) { // east value = int((16 * angle * 180 / M_PI) + 0.5); value |= 0xe000; } else { // west value = int((16 * (-angle) * 180 / M_PI) + 0.5); value |= 0xd000; } char cmd[] = { char(0xe0), 0x31, 0x6e, char(value / 256), char(value % 256) }; usleep(15000); backend->sendMessage(cmd, sizeof(cmd)); usleep(15000); moveRotor = true; break; } case DvbConfigBase::PositionsRotor: { char cmd[] = { char(0xe0), 0x31, 0x6b, char(config->lnbNumber) }; usleep(15000); backend->sendMessage(cmd, sizeof(cmd)); usleep(15000); moveRotor = true; break; } } // tune if (backend->tune(intermediate)) { if (!moveRotor) { setDeviceState(DeviceTuning); frontendTimeout = config->timeout; } else { setDeviceState(DeviceRotorMoving); frontendTimeout = 15000; } frontendTimer.start(100); discardBuffers(); } else { setDeviceState(DeviceTuning); autoTransponder.setTransmissionType(DvbTransponderBase::Invalid); setDeviceState(DeviceIdle); } } void DvbDevice::autoTune(const DvbTransponder &transponder) { DvbTransponderBase::TransmissionType transmissionType = transponder.getTransmissionType(); autoTransponder = transponder; if (transmissionType == DvbTransponderBase::DvbT) { DvbTTransponder *autoTTransponder = autoTransponder.as(); capabilities = backend->getCapabilities(); // we have to iterate over unsupported AUTO values if ((capabilities & DvbTFecAuto) == 0) { autoTTransponder->fecRateHigh = DvbTTransponder::Fec2_3; } if ((capabilities & DvbTGuardIntervalAuto) == 0) { autoTTransponder->guardInterval = DvbTTransponder::GuardInterval1_8; } if ((capabilities & DvbTModulationAuto) == 0) { autoTTransponder->modulation = DvbTTransponder::Qam64; } if ((capabilities & DvbTTransmissionModeAuto) == 0) { autoTTransponder->transmissionMode = DvbTTransponder::TransmissionMode8k; } isAuto = true; tune(autoTransponder); } else if (transmissionType == DvbTransponderBase::DvbT2) { // I guess all DVB-T2 devices support auto-detection isAuto = true; tune(autoTransponder); } else if (transmissionType == DvbTransponderBase::IsdbT) { // ISDB-T Currently, all ISDB-T tuners should support auto mode isAuto = true; tune(autoTransponder); } else { qCWarning(logDev, "Can't do auto-tune for %d", transmissionType); return; } isAuto = true; tune(autoTransponder); } bool DvbDevice::addPidFilter(int pid, DvbPidFilter *filter) { QMap::iterator it = filters.find(pid); if (it == filters.end()) { it = filters.insert(pid, DvbFilterInternal()); if (dataDumper != NULL) { it->filters.append(dataDumper); } } if (it->activeFilters == 0) { if (!backend->addPidFilter(pid)) { cleanUpFilters = true; return false; } } if (it->filters.contains(filter)) { qCInfo(logDev, "Using the same filter for the same pid more than once"); return true; } it->filters.append(filter); ++it->activeFilters; return true; } bool DvbDevice::addSectionFilter(int pid, DvbSectionFilter *filter) { QMap::iterator it = sectionFilters.find(pid); if (it == sectionFilters.end()) { it = sectionFilters.insert(pid, DvbSectionFilterInternal()); } if (it->activeSectionFilters == 0) { if (!addPidFilter(pid, &(*it))) { cleanUpFilters = true; return false; } } if (it->sectionFilters.contains(filter)) { qCInfo(logDev, "Using the same filter for the same pid more than once"); return true; } it->sectionFilters.append(filter); ++it->activeSectionFilters; return true; } void DvbDevice::removePidFilter(int pid, DvbPidFilter *filter) { QMap::iterator it = filters.find(pid); int index; if (it != filters.end()) { index = it->filters.indexOf(filter); } else { index = -1; } if (index < 0) { qCWarning(logDev, "Trying to remove a nonexistent filter"); return; } it->filters.replace(index, &dummyPidFilter); --it->activeFilters; if (it->activeFilters == 0) { backend->removePidFilter(pid); } cleanUpFilters = true; } void DvbDevice::removeSectionFilter(int pid, DvbSectionFilter *filter) { QMap::iterator it = sectionFilters.find(pid); int index; if (it != sectionFilters.end()) { index = it->sectionFilters.indexOf(filter); } else { index = -1; } if (index < 0) { qCWarning(logDev, "Trying to remove a nonexistent filter"); return; } it->sectionFilters.replace(index, &dummySectionFilter); --it->activeSectionFilters; if (it->activeSectionFilters == 0) { removePidFilter(pid, &(*it)); } cleanUpFilters = true; } void DvbDevice::startDescrambling(const QByteArray &pmtSectionData, QObject *user) { DvbPmtSection pmtSection(pmtSectionData); if (!pmtSection.isValid()) { qCWarning(logDev, "PMT section is invalid"); } int serviceId = pmtSection.programNumber(); if (!descramblingServices.contains(serviceId)) { backend->startDescrambling(pmtSectionData); } if (!descramblingServices.contains(serviceId, user)) { descramblingServices.insert(serviceId, user); } } void DvbDevice::stopDescrambling(const QByteArray &pmtSectionData, QObject *user) { DvbPmtSection pmtSection(pmtSectionData); if (!pmtSection.isValid()) { qCWarning(logDev, "PMT section is invalid"); } int serviceId = pmtSection.programNumber(); if (!descramblingServices.contains(serviceId, user)) { qCInfo(logDev, "Service has not been started while stop descrambling"); return; } descramblingServices.remove(serviceId, user); if (!descramblingServices.contains(serviceId)) { backend->stopDescrambling(serviceId); } } bool DvbDevice::isTuned() const { return backend->isTuned(); } bool DvbDevice::getProps(DvbTransponder &transponder) const { return backend->getProps(transponder); } float DvbDevice::getSignal(DvbBackendDevice::Scale &scale) const { return backend->getSignal(scale); } float DvbDevice::getSnr(DvbBackendDevice::Scale &scale) const { return backend->getSnr(scale); } DvbTransponder DvbDevice::getAutoTransponder() const { // FIXME query back information like frequency - tuning parameters - ... return autoTransponder; } bool DvbDevice::acquire(const DvbConfigBase *config_) { Q_ASSERT(deviceState == DeviceReleased); if (backend->acquire()) { config = config_; autoTransponder.setTransmissionType(DvbTransponderBase::Invalid); setDeviceState(DeviceIdle); return true; } return false; } void DvbDevice::reacquire(const DvbConfigBase *config_) { Q_ASSERT(deviceState != DeviceReleased); setDeviceState(DeviceReleased); stop(); config = config_; autoTransponder.setTransmissionType(DvbTransponderBase::Invalid); setDeviceState(DeviceIdle); } void DvbDevice::release() { setDeviceState(DeviceReleased); stop(); backend->release(); } void DvbDevice::enableDvbDump() { if (dataDumper != NULL) { return; } dataDumper = new DvbDataDumper(); QMap::iterator it = filters.begin(); QMap::iterator end = filters.end(); for (; it != end; ++it) { it->filters.append(dataDumper); } backend->enableDvbDump(); } void DvbDevice::frontendEvent() { DvbTransponderBase::TransmissionType transmissionType = autoTransponder.getTransmissionType(); if (backend->isTuned()) { qCDebug(logDvb, "tuning succeeded on %.2f MHz", backend->getFrqMHz()); frontendTimer.stop(); backend->getProps(autoTransponder); setDeviceState(DeviceTuned); return; } // FIXME progress bar when moving rotor frontendTimeout -= 100; if (frontendTimeout <= 0) { frontendTimer.stop(); if (!isAuto) { qCDebug(logDvb, "tuning failed on %.2f MHz", backend->getFrqMHz()); autoTransponder.setTransmissionType(DvbTransponderBase::Invalid); setDeviceState(DeviceIdle); return; } bool carry = true; /* * As ISDB-T always support auto-scan, we only need to simulate * it for DVB-T */ if (transmissionType == DvbTransponderBase::DvbT) { DvbBackendDevice::Scale scale; DvbTTransponder *autoTTransponder = autoTransponder.as(); float signal = backend->getSignal(scale); if ((scale != DvbBackendDevice::NotSupported) && (signal < 15)) { // signal too weak qCInfo(logDev, "Signal too weak on %.2f MHz", backend->getFrqMHz()); /* * FIXME: ignoring a too weak signal is not so easy, * as it depends on the scale, and scale is broken on * several drivers. Also, signal itself is not a good * indicator of the quality. Better to just print a * warning, and not fail. */ #if 0 autoTransponder.setTransmissionType(DvbTransponderBase::Invalid); setDeviceState(DeviceIdle); return; #endif } if (carry && ((capabilities & DvbTFecAuto) == 0)) { switch (autoTTransponder->fecRateHigh) { case DvbTTransponder::Fec2_3: autoTTransponder->fecRateHigh = DvbTTransponder::Fec3_4; carry = false; break; case DvbTTransponder::Fec3_4: autoTTransponder->fecRateHigh = DvbTTransponder::Fec1_2; carry = false; break; case DvbTTransponder::Fec1_2: autoTTransponder->fecRateHigh = DvbTTransponder::Fec5_6; carry = false; break; case DvbTTransponder::Fec5_6: autoTTransponder->fecRateHigh = DvbTTransponder::Fec7_8; carry = false; break; default: autoTTransponder->fecRateHigh = DvbTTransponder::Fec2_3; break; } } if (carry && ((capabilities & DvbTGuardIntervalAuto) == 0)) { switch (autoTTransponder->guardInterval) { case DvbTTransponder::GuardInterval1_8: autoTTransponder->guardInterval = DvbTTransponder::GuardInterval1_32; carry = false; break; case DvbTTransponder::GuardInterval1_32: autoTTransponder->guardInterval = DvbTTransponder::GuardInterval1_4; carry = false; break; case DvbTTransponder::GuardInterval1_4: autoTTransponder->guardInterval = DvbTTransponder::GuardInterval1_16; carry = false; break; case DvbTTransponder::GuardInterval1_16: case DvbTTransponder::GuardIntervalAuto: autoTTransponder->guardInterval = DvbTTransponder::GuardInterval1_8; break; } } if (carry && ((capabilities & DvbTModulationAuto) == 0)) { switch (autoTTransponder->modulation) { case DvbTTransponder::Qam64: autoTTransponder->modulation = DvbTTransponder::Qam16; carry = false; break; case DvbTTransponder::Qam16: autoTTransponder->modulation = DvbTTransponder::Qpsk; carry = false; break; case DvbTTransponder::Qpsk: case DvbTTransponder::ModulationAuto: autoTTransponder->modulation = DvbTTransponder::Qam64; break; } } if (carry && ((capabilities & DvbTTransmissionModeAuto) == 0)) { switch (autoTTransponder->transmissionMode) { case DvbTTransponder::TransmissionMode8k: autoTTransponder->transmissionMode = DvbTTransponder::TransmissionMode2k; carry = false; break; case DvbTTransponder::TransmissionMode2k: /* outcommented so that clearly no compatibility problem arises autoTTransponder->transmissionMode = DvbTTransponder::TransmissionMode4k; carry = false; break; */ case DvbTTransponder::TransmissionMode4k: case DvbTTransponder::TransmissionModeAuto: autoTTransponder->transmissionMode = DvbTTransponder::TransmissionMode8k; break; } } } if (!carry) { tune(autoTransponder); } else { qCDebug(logDvb, "tuning failed on %.2f MHz", backend->getFrqMHz());; setDeviceState(DeviceIdle); } } } void DvbDevice::setDeviceState(DeviceState newState) { if (deviceState != newState) { deviceState = newState; emit stateChanged(); } } void DvbDevice::discardBuffers() { dataChannelMutex.lock(); if (usedBuffersHead != NULL) { usedBuffersHead->size = 0; DvbDeviceDataBuffer *nextBuffer = usedBuffersHead->next; usedBuffersHead->next = NULL; if (nextBuffer != NULL) { nextBuffer->next = unusedBuffersHead; unusedBuffersHead = nextBuffer; } } dataChannelMutex.unlock(); } void DvbDevice::stop() { isAuto = false; frontendTimer.stop(); for (QMap::ConstIterator it = filters.constBegin(); it != filters.constEnd(); ++it) { foreach (DvbPidFilter *filter, it->filters) { if ((filter != &dummyPidFilter) && (filter != dataDumper)) { int pid = it.key(); qCDebug(logDvb, "removing pending filter %d", pid); removePidFilter(pid, filter); } } } for (QMap::ConstIterator it = sectionFilters.constBegin(); it != sectionFilters.constEnd(); ++it) { foreach (DvbSectionFilter *sectionFilter, it->sectionFilters) { if (sectionFilter != &dummySectionFilter) { int pid = it.key(); qCDebug(logDvb, "removing pending filter %d", pid); removeSectionFilter(pid, sectionFilter); } } } } DvbDataBuffer DvbDevice::getBuffer() { dataChannelMutex.lock(); DvbDeviceDataBuffer *buffer = unusedBuffersHead; if (buffer != NULL) { unusedBuffersHead = buffer->next; dataChannelMutex.unlock(); } else { dataChannelMutex.unlock(); buffer = new DvbDeviceDataBuffer; } return DvbDataBuffer(buffer->data, sizeof(buffer->data)); } void DvbDevice::writeBuffer(const DvbDataBuffer &dataBuffer) { DvbDeviceDataBuffer *buffer = reinterpret_cast(dataBuffer.data); Q_ASSERT(buffer->data == dataBuffer.data); if (dataBuffer.dataSize > 0) { buffer->size = dataBuffer.dataSize; dataChannelMutex.lock(); bool wakeUp = false; if (usedBuffersHead != NULL) { usedBuffersTail->next = buffer; } else { usedBuffersHead = buffer; wakeUp = true; } usedBuffersTail = buffer; usedBuffersTail->next = NULL; dataChannelMutex.unlock(); if (wakeUp) { QCoreApplication::postEvent(this, new QEvent(QEvent::User)); } } else { dataChannelMutex.lock(); buffer->next = unusedBuffersHead; unusedBuffersHead = buffer; dataChannelMutex.unlock(); } } void DvbDevice::customEvent(QEvent *) { if (cleanUpFilters) { cleanUpFilters = false; { QMap::iterator it = filters.begin(); QMap::iterator end = filters.end(); while (it != end) { if (it->activeFilters == 0) { it = filters.erase(it); } else { it->filters.removeAll(&dummyPidFilter); ++it; } } } { QMap::iterator it = sectionFilters.begin(); QMap::iterator end = sectionFilters.end(); while (it != end) { if (it->activeSectionFilters == 0) { it = sectionFilters.erase(it); } else { it->sectionFilters.removeAll(&dummySectionFilter); ++it; } } } } DvbDeviceDataBuffer *buffer = NULL; while (true) { dataChannelMutex.lock(); if (buffer != NULL) { usedBuffersHead = buffer->next; buffer->next = unusedBuffersHead; unusedBuffersHead = buffer; } buffer = usedBuffersHead; dataChannelMutex.unlock(); if (buffer == NULL) { break; } for (int i = 0; i < buffer->size; i += 188) { char *packet = (buffer->data + i); if ((packet[1] & 0x80) != 0) { // transport error indicator continue; } int pid = ((static_cast(packet[1]) << 8) | static_cast(packet[2])) & ((1 << 13) - 1); QMap::const_iterator it = filters.constFind(pid); if (it == filters.constEnd()) { continue; } const QList &pidFilters = it->filters; int pidFiltersSize = pidFilters.size(); for (int j = 0; j < pidFiltersSize; ++j) { pidFilters.at(j)->processData(packet); } } } } diff --git a/src/dvb/dvbdevice.h b/src/dvb/dvbdevice.h index f3c184e..11a7fd8 100644 --- a/src/dvb/dvbdevice.h +++ b/src/dvb/dvbdevice.h @@ -1,155 +1,155 @@ /* * dvbdevice.h * * Copyright (C) 2007-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DVBDEVICE_H #define DVBDEVICE_H #include #include #include #include #include "dvbbackenddevice.h" #include "dvbtransponder.h" class DvbConfigBase; class DvbDataDumper; class DvbDeviceDataBuffer; class DvbFilterInternal; class DvbSectionFilterInternal; class DvbDummyPidFilter : public DvbPidFilter { public: DvbDummyPidFilter() { } ~DvbDummyPidFilter() { } - void processData(const char [188]) { } + void processData(const char [188]) override { } }; class DvbDummySectionFilter : public DvbSectionFilter { public: DvbDummySectionFilter() { } ~DvbDummySectionFilter() { } - void processSection(const char *, int) { } + void processSection(const char *, int) override { } }; // FIXME make DvbDevice shared ... class DvbDevice : public QObject, public DvbFrontendDevice { Q_OBJECT public: enum DeviceState { DeviceReleased, DeviceIdle, DeviceRotorMoving, DeviceTuning, DeviceTuned // FIXME introduce a TuningFailed state }; DvbDevice(DvbBackendDevice *backend_, QObject *parent); ~DvbDevice(); const DvbBackendDevice *getBackendDevice() const { return backend; } DeviceState getDeviceState() const { return deviceState; } QList getLnbSatModels() const { return backend->getLnbSatModels(); } TransmissionTypes getTransmissionTypes() const; QString getDeviceId() const; QString getFrontendName() const; void tune(const DvbTransponder &transponder); void autoTune(const DvbTransponder &transponder); - bool addPidFilter(int pid, DvbPidFilter *filter); - bool addSectionFilter(int pid, DvbSectionFilter *filter); - void removePidFilter(int pid, DvbPidFilter *filter); - void removeSectionFilter(int pid, DvbSectionFilter *filter); + bool addPidFilter(int pid, DvbPidFilter *filter) override; + bool addSectionFilter(int pid, DvbSectionFilter *filter) override; + void removePidFilter(int pid, DvbPidFilter *filter) override; + void removeSectionFilter(int pid, DvbSectionFilter *filter) override; void startDescrambling(const QByteArray &pmtSectionData, QObject *user); void stopDescrambling(const QByteArray &pmtSectionData, QObject *user); bool isTuned() const; bool getProps(DvbTransponder &transponder) const; float getSignal(DvbBackendDevice::Scale &scale) const; float getSnr(DvbBackendDevice::Scale &scale) const; DvbTransponder getAutoTransponder() const; /* * management functions (must be only called by DvbManager) */ bool acquire(const DvbConfigBase *config_); void reacquire(const DvbConfigBase *config_); void release(); void enableDvbDump(); signals: void stateChanged(); private slots: void frontendEvent(); private: void setDeviceState(DeviceState newState); void discardBuffers(); void stop(); void processData(const char data[188]); - DvbDataBuffer getBuffer(); - void writeBuffer(const DvbDataBuffer &dataBuffer); - void customEvent(QEvent *); + DvbDataBuffer getBuffer() override; + void writeBuffer(const DvbDataBuffer &dataBuffer) override; + void customEvent(QEvent *) override; DvbBackendDevice *backend; DeviceState deviceState; QExplicitlySharedDataPointer config; int frontendTimeout; QTimer frontendTimer; QMap filters; QMap sectionFilters; DvbDummyPidFilter dummyPidFilter; DvbDummySectionFilter dummySectionFilter; DvbDataDumper *dataDumper; bool cleanUpFilters; QMultiMap descramblingServices; bool isAuto; DvbTransponder autoTransponder; Capabilities capabilities; DvbDeviceDataBuffer *unusedBuffersHead; DvbDeviceDataBuffer *usedBuffersHead; DvbDeviceDataBuffer *usedBuffersTail; QMutex dataChannelMutex; }; #endif /* DVBDEVICE_H */ diff --git a/src/dvb/dvbdevice_linux.h b/src/dvb/dvbdevice_linux.h index b8b68f4..1d0a2e9 100644 --- a/src/dvb/dvbdevice_linux.h +++ b/src/dvb/dvbdevice_linux.h @@ -1,139 +1,139 @@ /* * dvbdevice_linux.h * * Copyright (C) 2007-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DVBDEVICE_LINUX_H #define DVBDEVICE_LINUX_H #include #include "dvbbackenddevice.h" #include "dvbcam_linux.h" extern "C" { #include #include #include #include } class DvbLinuxDevice : public QThread, public DvbBackendDevice { public: explicit DvbLinuxDevice(QObject *parent); ~DvbLinuxDevice(); bool isReady() const; void startDevice(const QString &deviceId_); void startCa(); void stopCa(); void stopDevice(); - void enableDvbDump(); - QString getDeviceId(); - QString getFrontendName(); + void enableDvbDump() override; + QString getDeviceId() override; + QString getFrontendName() override; QString caPath; QString caUdi; QString demuxPath; QString demuxUdi; QString dvrPath; QString dvrUdi; int adapter; int index; int numDemux; struct dvb_v5_fe_parms *dvbv5_parms; QString frontendPath; QString frontendUdi; protected: - TransmissionTypes getTransmissionTypes(); - Capabilities getCapabilities(); - void setFrontendDevice(DvbFrontendDevice *frontend_); - void setDeviceEnabled(bool enabled_); - bool acquire(); - bool setHighVoltage(int higherVoltage); - bool sendMessage(const char *message, int length); - bool sendBurst(SecBurst burst); - bool satSetup(QString lnbModel, int satNumber, int bpf); - bool tune(const DvbTransponder &transponder); // discards obsolete data - bool getProps(DvbTransponder &transponder); - bool isTuned(); - float getFrqMHz(); - float getSignal(Scale &scale); - float getSnr(DvbBackendDevice::Scale &scale); - bool addPidFilter(int pid); - void removePidFilter(int pid); - void startDescrambling(const QByteArray &pmtSectionData); - void stopDescrambling(int serviceId); - void release(); + TransmissionTypes getTransmissionTypes() override; + Capabilities getCapabilities() override; + void setFrontendDevice(DvbFrontendDevice *frontend_) override; + void setDeviceEnabled(bool enabled_) override; + bool acquire() override; + bool setHighVoltage(int higherVoltage) override; + bool sendMessage(const char *message, int length) override; + bool sendBurst(SecBurst burst) override; + bool satSetup(QString lnbModel, int satNumber, int bpf) override; + bool tune(const DvbTransponder &transponder) override; // discards obsolete data + bool getProps(DvbTransponder &transponder) override; + bool isTuned() override; + float getFrqMHz() override; + float getSignal(Scale &scale) override; + float getSnr(DvbBackendDevice::Scale &scale) override; + bool addPidFilter(int pid) override; + void removePidFilter(int pid) override; + void startDescrambling(const QByteArray &pmtSectionData) override; + void stopDescrambling(int serviceId) override; + void release() override; private: void startDvr(); void stopDvr(); - void run(); + void run() override; bool ready; QString deviceId; QString frontendName; TransmissionTypes transmissionTypes; Capabilities capabilities; DvbFrontendDevice *frontend; bool enabled; QMap dmxFds; float freqMHz; int verbose; int dvrFd; int dvrPipe[2]; DvbDataBuffer dvrBuffer; DvbLinuxCam cam; }; class DvbDeviceMonitor; class DvbLinuxDeviceManager : public QObject { Q_OBJECT public: explicit DvbLinuxDeviceManager(QObject *parent); ~DvbLinuxDeviceManager(); void componentAdded(QString node, int adapter, int index); void componentRemoved(QString node, int adapter, int index); public slots: void doColdPlug(); signals: void requestBuiltinDeviceManager(QObject *&bultinDeviceManager); void deviceAdded(DvbBackendDevice *device); void deviceRemoved(DvbBackendDevice *device); private slots: void componentAdded(const QString &udi); void componentRemoved(const QString &udi); private: int readSysAttr(const QString &path); QMap devices; QMap udis; class DvbDeviceMonitor *monitor; }; #endif /* DVBDEVICE_LINUX_H */ diff --git a/src/dvb/dvbepg.h b/src/dvb/dvbepg.h index bdba3f5..4187810 100644 --- a/src/dvb/dvbepg.h +++ b/src/dvb/dvbepg.h @@ -1,315 +1,315 @@ /* * dvbepg.h * * Copyright (C) 2009-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DVBEPG_H #define DVBEPG_H #include "dvbrecording.h" class AtscEpgFilter; class DvbDevice; class DvbEpgFilter; #define FIRST_LANG "first" class DvbEpgLangEntry { public: QString title; QString subheading; QString details; }; class DvbEpgEntry : public SharedData { public: enum EitType { EitActualTsPresentFollowing = 0, EitOtherTsPresentFollowing = 1, EitActualTsSchedule = 2, EitOtherTsSchedule = 3, EitLast = 3 }; DvbEpgEntry(): type(EitActualTsSchedule) { } explicit DvbEpgEntry(const DvbSharedChannel &channel_) : channel(channel_) { } ~DvbEpgEntry() { } // checks that all variables are ok bool validate() const; DvbSharedChannel channel; EitType type; QDateTime begin; // UTC QTime duration; QString content; QString parental; // ISO 639-2 language-dependent entries QHash langEntry; DvbSharedRecording recording; QString title(QString lang = QString()) const { QString s; if (!lang.isEmpty()) { /* * Only return the user requested data * ISO-639-2 code if the title is filled. * * If it isn't, show first language */ if (langEntry[lang].title.isEmpty()) lang = FIRST_LANG; else if (lang != FIRST_LANG) return langEntry[lang].title; } QHashIterator i(langEntry); bool first = true; while (i.hasNext()) { i.next(); QString code = i.key(); DvbEpgLangEntry entry = i.value(); if (!entry.title.isEmpty()) { if (first) first = false; else s += "/"; if (langEntry.size() > 1 && (lang != FIRST_LANG && code != FIRST_LANG)) { s += code; s += ": "; } s += entry.title; } if (lang == FIRST_LANG) break; } return s; } QString subheading(QString lang = QString()) const { QString s; if (!lang.isEmpty()) { /* * Only return the user requested data * ISO-639-2 code if the subheading is filled. * * If it isn't, show first language */ if (langEntry[lang].subheading.isEmpty()) lang = FIRST_LANG; else if (lang != FIRST_LANG) return langEntry[lang].subheading; } QHashIterator i(langEntry); bool first = true; while (i.hasNext()) { i.next(); QString code = i.key(); DvbEpgLangEntry entry = i.value(); if (!entry.subheading.isEmpty()) { if (first) first = false; else s += "/"; if (langEntry.size() > 1 && (lang != FIRST_LANG && code != FIRST_LANG)) { s += code; s += ": "; } s += entry.subheading; } if (lang == FIRST_LANG) break; } return s; } QString details(QString lang = QString()) const { QString s; if (!lang.isEmpty()) { /* * Only return the user requested data * ISO-639-2 code if the details are filled. * * If it isn't, show first language */ if (langEntry[lang].details.isEmpty()) lang = FIRST_LANG; else if (lang != FIRST_LANG) return langEntry[lang].details; } QHashIterator i(langEntry); bool first = true; while (i.hasNext()) { i.next(); QString code = i.key(); DvbEpgLangEntry entry = i.value(); if (!entry.details.isEmpty()) { if (first) first = false; else s += "\n\n"; if (langEntry.size() > 1 && (lang != FIRST_LANG && code != FIRST_LANG)) { s += code; s += ": "; } s += entry.details; } if (lang == FIRST_LANG) break; } return s; } // Check only the user-visible elements bool operator==(const DvbEpgEntry &other) const { if (channel != other.channel) return false; if (begin != other.begin) return false; if (duration != other.duration) return false; if (content != other.content) return false; QHashIterator i(langEntry); while (i.hasNext()) { i.next(); QString code = i.key(); if (!other.langEntry.contains(code)) return false; DvbEpgLangEntry thisEntry = i.value(); DvbEpgLangEntry otherEntry = other.langEntry[code]; if (thisEntry.title != otherEntry.title) return false; if (thisEntry.subheading != otherEntry.subheading) return false; if (thisEntry.details != otherEntry.details) return false; // If first language matches, assume entries are identical return true; } return true; } }; typedef ExplicitlySharedDataPointer DvbSharedEpgEntry; Q_DECLARE_TYPEINFO(DvbSharedEpgEntry, Q_MOVABLE_TYPE); class DvbEpgEntryId { public: explicit DvbEpgEntryId(const DvbEpgEntry *entry_) : entry(entry_) { } explicit DvbEpgEntryId(const DvbSharedEpgEntry &entry_) : entry(entry_.constData()) { } ~DvbEpgEntryId() { } // compares entries, 'recording' is ignored // if one 'details' is empty, 'details' is ignored bool operator<(const DvbEpgEntryId &other) const; private: const DvbEpgEntry *entry; }; class DvbEpgModel : public QObject { Q_OBJECT typedef QMap::Iterator Iterator; typedef QMap::ConstIterator ConstIterator; public: DvbEpgModel(DvbManager *manager_, QObject *parent); ~DvbEpgModel(); QMap getEntries() const; QMap getRecordings() const; void setRecordings(const QMap map); QHash getEpgChannels() const; QList getCurrentNext(const DvbSharedChannel &channel) const; DvbSharedEpgEntry addEntry(const DvbEpgEntry &entry); void scheduleProgram(const DvbSharedEpgEntry &entry, int extraSecondsBefore, int extraSecondsAfter, bool checkForRecursion=false, int priority=10); void startEventFilter(DvbDevice *device, const DvbSharedChannel &channel); void stopEventFilter(DvbDevice *device, const DvbSharedChannel &channel); signals: void entryAdded(const DvbSharedEpgEntry &entry); // updating doesn't change the entry pointer (modifies existing content) void entryAboutToBeUpdated(const DvbSharedEpgEntry &entry); void entryUpdated(const DvbSharedEpgEntry &entry); void entryRemoved(const DvbSharedEpgEntry &entry); void epgChannelAdded(const DvbSharedChannel &channel); void epgChannelRemoved(const DvbSharedChannel &channel); void languageAdded(const QString lang); private slots: void channelAboutToBeUpdated(const DvbSharedChannel &channel); void channelUpdated(const DvbSharedChannel &channel); void channelRemoved(const DvbSharedChannel &channel); void recordingRemoved(const DvbSharedRecording &recording); private: - void timerEvent(QTimerEvent *event); + void timerEvent(QTimerEvent *event) override; void Debug(QString text, const DvbSharedEpgEntry &entry); Iterator removeEntry(Iterator it); DvbManager *manager; QDateTime currentDateTimeUtc; QMap entries; QMap recordings; QHash epgChannels; QList > dvbEpgFilters; QList > atscEpgFilters; DvbChannel updatingChannel; bool hasPendingOperation; }; #endif /* DVBEPG_H */ diff --git a/src/dvb/dvbepg_p.h b/src/dvb/dvbepg_p.h index 5f2ebd8..1b06635 100644 --- a/src/dvb/dvbepg_p.h +++ b/src/dvb/dvbepg_p.h @@ -1,127 +1,127 @@ /* * dvbepg_p.h * * Copyright (C) 2009-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DVBEPG_P_H #define DVBEPG_P_H #include "dvbbackenddevice.h" #include "dvbepg.h" #include "dvbsi.h" class DvbContentDescriptor; class DvbParentalRatingDescriptor; class DvbEpgLangEntry; class DvbEpgFilter : public QSharedData, public DvbSectionFilter { public: DvbEpgFilter(DvbManager *manager, DvbDevice *device_, const DvbSharedChannel &channel); ~DvbEpgFilter(); DvbDevice *device; QString source; DvbTransponder transponder; private: Q_DISABLE_COPY(DvbEpgFilter) static QTime bcdToTime(int bcd); DvbEpgLangEntry *getLangEntry(DvbEpgEntry &epgEntry, int code1, int code2, int code3, bool add_code = true, QString *code = NULL); - void processSection(const char *data, int size); + void processSection(const char *data, int size) override; QString getContent(DvbContentDescriptor &descriptor); QString getParental(DvbParentalRatingDescriptor &descriptor); DvbChannelModel *channelModel; DvbEpgModel *epgModel; DvbManager *manager; }; class AtscEpgMgtFilter : public DvbSectionFilter { public: explicit AtscEpgMgtFilter(AtscEpgFilter *epgFilter_) : epgFilter(epgFilter_) { } ~AtscEpgMgtFilter() { } private: Q_DISABLE_COPY(AtscEpgMgtFilter) - void processSection(const char *data, int size); + void processSection(const char *data, int size) override; AtscEpgFilter *epgFilter; }; class AtscEpgEitFilter : public DvbSectionFilter { public: explicit AtscEpgEitFilter(AtscEpgFilter *epgFilter_) : epgFilter(epgFilter_) { } ~AtscEpgEitFilter() { } private: Q_DISABLE_COPY(AtscEpgEitFilter) - void processSection(const char *data, int size); + void processSection(const char *data, int size) override; AtscEpgFilter *epgFilter; }; class AtscEpgEttFilter : public DvbSectionFilter { public: explicit AtscEpgEttFilter(AtscEpgFilter *epgFilter_) : epgFilter(epgFilter_) { } ~AtscEpgEttFilter() { } private: Q_DISABLE_COPY(AtscEpgEttFilter) - void processSection(const char *data, int size); + void processSection(const char *data, int size) override; AtscEpgFilter *epgFilter; }; class AtscEpgFilter : public QSharedData { friend class AtscEpgMgtFilter; friend class AtscEpgEitFilter; friend class AtscEpgEttFilter; public: AtscEpgFilter(DvbManager *manager, DvbDevice *device_, const DvbSharedChannel &channel); ~AtscEpgFilter(); DvbDevice *device; QString source; DvbTransponder transponder; private: Q_DISABLE_COPY(AtscEpgFilter) void processMgtSection(const char *data, int size); void processEitSection(const char *data, int size); void processEttSection(const char *data, int size); DvbChannelModel *channelModel; DvbEpgModel *epgModel; AtscEpgMgtFilter mgtFilter; AtscEpgEitFilter eitFilter; AtscEpgEttFilter ettFilter; QList eitPids; QList ettPids; QMap epgEntries; }; #endif /* DVBEPG_P_H */ diff --git a/src/dvb/dvbepgdialog_p.h b/src/dvb/dvbepgdialog_p.h index 7eee3e9..4bfbbcf 100644 --- a/src/dvb/dvbepgdialog_p.h +++ b/src/dvb/dvbepgdialog_p.h @@ -1,153 +1,153 @@ /* * dvbepgdialog_p.h * * Copyright (C) 2009-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DVBEPGDIALOG_P_H #define DVBEPGDIALOG_P_H #include "dvbchanneldialog.h" #include "dvbepg.h" class DvbEpgEntryLessThan { public: DvbEpgEntryLessThan() { } ~DvbEpgEntryLessThan() { } enum SortOrder { }; SortOrder getSortOrder() const { return SortOrder(); } void setSortOrder(SortOrder sortOrder_) { Q_UNUSED(sortOrder_) } bool operator()(const DvbSharedEpgEntry &x, const DvbSharedEpgEntry &y) const; }; class DvbEpgChannelTableModelHelper { public: DvbEpgChannelTableModelHelper() { } ~DvbEpgChannelTableModelHelper() { } typedef DvbSharedChannel ItemType; typedef DvbChannelLessThan LessThanType; int columnCount() const { return 1; } bool filterAcceptsItem(const DvbSharedChannel &channel) const { Q_UNUSED(channel) return true; } }; class DvbEpgChannelTableModel : public TableModel { Q_OBJECT private: DvbManager *manager; public: explicit DvbEpgChannelTableModel(QObject *parent); ~DvbEpgChannelTableModel(); void setManager(DvbManager *manager); void setViewMode(bool enableEmptyEpgChannels); - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; private slots: void epgChannelAdded(const DvbSharedChannel &channel); void epgChannelRemoved(const DvbSharedChannel &channel); }; class DvbEpgTableModelHelper { public: DvbEpgTableModelHelper() : filterType(ChannelFilter) { } ~DvbEpgTableModelHelper() { } typedef DvbSharedEpgEntry ItemType; typedef DvbEpgEntryLessThan LessThanType; enum FilterType { ChannelFilter, ContentFilter }; int columnCount() const { return 4; } bool filterAcceptsItem(const DvbSharedEpgEntry &epgEntry) const; DvbSharedChannel channelFilter; QStringMatcher contentFilter; FilterType filterType; private: Q_DISABLE_COPY(DvbEpgTableModelHelper) }; class DvbEpgTableModel : public TableModel { Q_OBJECT public: explicit DvbEpgTableModel(QObject *parent); ~DvbEpgTableModel(); void setEpgModel(DvbEpgModel *epgModel_); void setChannelFilter(const DvbSharedChannel &channel); void setLanguage(QString lang); - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; public slots: void setContentFilter(const QString &pattern); private slots: void entryAdded(const DvbSharedEpgEntry &entry); void entryAboutToBeUpdated(const DvbSharedEpgEntry &entry); void entryUpdated(const DvbSharedEpgEntry &entry); void entryRemoved(const DvbSharedEpgEntry &entry); private: - void customEvent(QEvent *event); + void customEvent(QEvent *event) override; DvbEpgModel *epgModel; bool contentFilterEventPending; QString currentLanguage; }; #endif /* DVBEPGDIALOG_P_H */ diff --git a/src/dvb/dvbliveview_p.h b/src/dvb/dvbliveview_p.h index 6757e85..7efb968 100644 --- a/src/dvb/dvbliveview_p.h +++ b/src/dvb/dvbliveview_p.h @@ -1,144 +1,144 @@ /* * dvbliveview_p.h * * Copyright (C) 2007-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DVBLIVEVIEW_P_H #define DVBLIVEVIEW_P_H #include #include "../mediawidget.h" #include "../osdwidget.h" #include "dvbepg.h" #include "dvbsi.h" #include "dvbmanager.h" class QSocketNotifier; class DvbOsd : public OsdObject { public: enum OsdLevel { Off, ShortOsd, LongOsd }; DvbOsd() : level(Off) { } ~DvbOsd() { } void init(DvbManager *manager_, OsdLevel level_, const QString &channelName_, const QList &epgEntries); OsdLevel level; private: - QPixmap paintOsd(QRect &rect, const QFont &font, Qt::LayoutDirection direction); + QPixmap paintOsd(QRect &rect, const QFont &font, Qt::LayoutDirection direction) override; QString channelName; DvbEpgEntry firstEntry; DvbEpgEntry secondEntry; DvbManager *manager; }; class DvbLiveViewInternal : public QObject, public DvbPidFilter, public MediaSource { Q_OBJECT public: explicit DvbLiveViewInternal(QObject *parent); ~DvbLiveViewInternal(); void resetPipe(); - bool overrideAudioStreams() const { return !audioStreams.isEmpty(); } - QStringList getAudioStreams() const { return audioStreams; } - QStringList getSubtitles() const { return QStringList(); } - int getCurrentAudioStream() const { return currentAudioStream; } - int getCurrentSubtitle() const { return currentSubtitle; } + bool overrideAudioStreams() const override { return !audioStreams.isEmpty(); } + QStringList getAudioStreams() const override { return audioStreams; } + QStringList getSubtitles() const override { return QStringList(); } + int getCurrentAudioStream() const override { return currentAudioStream; } + int getCurrentSubtitle() const override { return currentSubtitle; } - void setCurrentAudioStream(int currentAudioStream_) + void setCurrentAudioStream(int currentAudioStream_) override { currentAudioStream = currentAudioStream_; emit currentAudioStreamChanged(currentAudioStream); } - void setCurrentSubtitle(int currentSubtitle_) + void setCurrentSubtitle(int currentSubtitle_) override { currentSubtitle = currentSubtitle_; emit currentSubtitleChanged(currentSubtitle); } - bool overrideCaption() const { return true; } + bool overrideCaption() const override { return true; } - QString getDefaultCaption() const { return channelName; } + QString getDefaultCaption() const override { return channelName; } - Type getType() const { return Dvb; } + Type getType() const override { return Dvb; } - QUrl getUrl() const { return url; } + QUrl getUrl() const override { return url; } void updateUrl() { if (timeShiftFile.isOpen()) url = QUrl::fromLocalFile(timeShiftFile.fileName()); else url = QUrl::fromLocalFile(fileName); } - virtual void validateCurrentTotalTime(int ¤tTime, int &totalTime) const; - bool hideCurrentTotalTime() const { return !timeshift; } + virtual void validateCurrentTotalTime(int ¤tTime, int &totalTime) const override; + bool hideCurrentTotalTime() const override { return !timeshift; } MediaWidget *mediaWidget; QString channelName; DvbPmtFilter pmtFilter; QByteArray pmtSectionData; DvbSectionGenerator patGenerator; DvbSectionGenerator pmtGenerator; QByteArray buffer; QFile timeShiftFile; QString fileName; DvbOsd dvbOsd; bool emptyBuffer; QTime startTime; bool timeshift; QStringList audioStreams; int currentAudioStream; int currentSubtitle; int retryCounter; signals: void currentAudioStreamChanged(int currentAudioStream); void currentSubtitleChanged(int currentSubtitle); - void replay(); - void playbackFinished(); - void playbackStatusChanged(MediaWidget::PlaybackStatus playbackStatus); - void previous(); - void next(); + void replay() override; + void playbackFinished() override; + void playbackStatusChanged(MediaWidget::PlaybackStatus playbackStatus) override; + void previous() override; + void next() override; private slots: void writeToPipe(); private: - void processData(const char data[188]); + void processData(const char data[188]) override; QUrl url; int readFd; int writeFd; QSocketNotifier *notifier; QList buffers; }; #endif /* DVBLIVEVIEW_P_H */ diff --git a/src/dvb/dvbrecording.h b/src/dvb/dvbrecording.h index 5172af3..b865c93 100644 --- a/src/dvb/dvbrecording.h +++ b/src/dvb/dvbrecording.h @@ -1,130 +1,130 @@ /* * dvbrecording.h * * Copyright (C) 2009-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DVBRECORDING_H #define DVBRECORDING_H #include #include #include "dvbchannel.h" class DvbManager; class DvbRecordingFile; class DvbEpgEntry; class DvbRecording : public SharedData, public SqlKey { public: DvbRecording() : repeat(0), status(Inactive) { disabled = false; } ~DvbRecording() { } // checks that all variables are ok and updates 'end' // 'sqlKey' and 'status' are ignored bool validate(); enum Status { Inactive, Recording, Error }; QString name; QString filename; QString subheading; QString details; DvbSharedChannel channel; QDateTime begin; // UTC QDateTime end; // UTC, read-only QTime duration; QDateTime beginEPG; // What EPG claims to be the beginning of the program, local time QDateTime endEPG; QTime durationEPG; int repeat; // (1 << 0) (monday) | (1 << 1) (tuesday) | ... | (1 << 6) (sunday) int priority; bool disabled; Status status; // read-only }; typedef ExplicitlySharedDataPointer DvbSharedRecording; Q_DECLARE_TYPEINFO(DvbSharedRecording, Q_MOVABLE_TYPE); class DvbRecordingModel : public QObject, private SqlInterface { Q_OBJECT public: DvbRecordingModel(DvbManager *manager_, QObject *parent); ~DvbRecordingModel(); bool hasRecordings() const; void setEmptyAction(QString emptyAction); QString getEmptyAction(); bool hasActiveRecordings() const; DvbSharedRecording findRecordingByKey(const SqlKey &sqlKey) const; QMap getRecordings() const; QList getUnwantedRecordings() const; DvbSharedRecording addRecording(DvbRecording &recording, bool checkForRecursion=false); void updateRecording(DvbSharedRecording recording, DvbRecording &modifiedRecording); void removeRecording(DvbSharedRecording recording); void addToUnwantedRecordings(DvbSharedRecording recording); void findNewRecordings(); void removeDuplicates(); void executeActionAfterRecording(DvbRecording recording); DvbRecording getCurrentRecording(); void setCurrentRecording(DvbRecording _currentRecording); void disableLessImportant(DvbSharedRecording &recording1, DvbSharedRecording &recording2); bool areInConflict(DvbSharedRecording recording1, DvbSharedRecording recording2); bool isInConflictWithAll(DvbSharedRecording rec, QList recList); int getNumberOfLeastImportants(QList recList); DvbSharedRecording getLeastImportant(QList recList); void disableLeastImportants(QList recList); void disableConflicts(); int getSecondsUntilNextRecording() const; bool isScanWhenIdle() const; bool shouldWeScanChannels() const; void scanChannels(); signals: void recordingAdded(const DvbSharedRecording &recording); // updating doesn't change the recording pointer (modifies existing content) void recordingAboutToBeUpdated(const DvbSharedRecording &recording); void recordingUpdated(const DvbSharedRecording &recording); void recordingRemoved(const DvbSharedRecording &recording); private: - void timerEvent(QTimerEvent *event); + void timerEvent(QTimerEvent *event) override; - void bindToSqlQuery(SqlKey sqlKey, QSqlQuery &query, int index) const; - bool insertFromSqlQuery(SqlKey sqlKey, const QSqlQuery &query, int index); + void bindToSqlQuery(SqlKey sqlKey, QSqlQuery &query, int index) const override; + bool insertFromSqlQuery(SqlKey sqlKey, const QSqlQuery &query, int index) override; bool updateStatus(DvbRecording &recording); bool existsSimilarRecording(DvbEpgEntry recording); DvbManager *manager; QMap recordings; QList unwantedRecordings; QMap > recordingFiles; bool hasPendingOperation; DvbRecording currentRecording; }; void delay(int seconds); #endif /* DVBRECORDING_H */ diff --git a/src/dvb/dvbrecording_p.h b/src/dvb/dvbrecording_p.h index 7710bee..9a81168 100644 --- a/src/dvb/dvbrecording_p.h +++ b/src/dvb/dvbrecording_p.h @@ -1,66 +1,66 @@ /* * dvbrecording_p.h * * Copyright (C) 2009-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DVBRECORDING_P_H #define DVBRECORDING_P_H #include #include #include "dvbchannel.h" #include "dvbsi.h" class DvbDevice; class DvbManager; class DvbRecording; class DvbRecordingFile : private QObject, public QSharedData, private DvbPidFilter { Q_OBJECT public: explicit DvbRecordingFile(DvbManager *manager_); ~DvbRecordingFile(); // start() returns true if the recording is already running bool start(DvbRecording &recording); void stop(); private slots: void deviceStateChanged(); void pmtSectionChanged(const QByteArray &pmtSectionData_); void insertPatPmt(); private: - void processData(const char data[188]); + void processData(const char data[188]) override; DvbManager *manager; DvbSharedChannel channel; QFile file; QList buffers; DvbDevice *device; QList pids; DvbPmtFilter pmtFilter; QByteArray pmtSectionData; DvbSectionGenerator patGenerator; DvbSectionGenerator pmtGenerator; QTimer patPmtTimer; bool pmtValid; }; #endif /* DVBRECORDING_P_H */ diff --git a/src/dvb/dvbrecordingdialog_p.h b/src/dvb/dvbrecordingdialog_p.h index a8432be..c0f5fb1 100644 --- a/src/dvb/dvbrecordingdialog_p.h +++ b/src/dvb/dvbrecordingdialog_p.h @@ -1,148 +1,148 @@ /* * dvbrecordingdialog_p.h * * Copyright (C) 2009-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DVBRECORDINGDIALOG_P_H #define DVBRECORDINGDIALOG_P_H #include #include "../tablemodel.h" #include "dvbrecording.h" class QCheckBox; class QComboBox; class QLineEdit; class DateTimeEdit; class DurationEdit; class QDialogButtonBox; class DvbRecordingLessThan { public: DvbRecordingLessThan() { // specify each column exactly once sortOrder[0] = BeginAscending; sortOrder[1] = ChannelAscending; sortOrder[2] = NameAscending; sortOrder[3] = DurationAscending; } ~DvbRecordingLessThan() { } enum SortOrder { NameAscending = 0, NameDescending = 1, ChannelAscending = 2, ChannelDescending = 3, BeginAscending = 4, BeginDescending = 5, DurationAscending = 6, DurationDescending = 7 }; SortOrder getSortOrder() const { return sortOrder[0]; } void setSortOrder(SortOrder sortOrder_); bool operator()(const DvbSharedRecording &x, const DvbSharedRecording &y) const; private: SortOrder sortOrder[4]; }; class DvbRecordingTableModelHelper { public: DvbRecordingTableModelHelper() { } ~DvbRecordingTableModelHelper() { } typedef DvbSharedRecording ItemType; typedef DvbRecordingLessThan LessThanType; int columnCount() const { return 5; } bool filterAcceptsItem(const DvbSharedRecording &recording) const { Q_UNUSED(recording) return true; } }; class DvbRecordingTableModel : public TableModel { Q_OBJECT public: explicit DvbRecordingTableModel(QObject *parent); ~DvbRecordingTableModel(); void setRecordingModel(DvbRecordingModel *recordingModel_); - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - QVariant data(const QModelIndex &index, int role) const; - void sort(int column, Qt::SortOrder order); + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + QVariant data(const QModelIndex &index, int role) const override; + void sort(int column, Qt::SortOrder order) override; private slots: void recordingAdded(const DvbSharedRecording &recording); void recordingAboutToBeUpdated(const DvbSharedRecording &recording); void recordingUpdated(const DvbSharedRecording &recording); void recordingRemoved(const DvbSharedRecording &recording); private: DvbRecordingModel *recordingModel; }; class DvbRecordingEditor : public QDialog { Q_OBJECT public: DvbRecordingEditor(DvbManager *manager_, const DvbSharedRecording &recording_, QWidget *parent); ~DvbRecordingEditor(); private slots: void beginChanged(const QDateTime &begin); void durationChanged(const QTime &duration); void endChanged(const QDateTime &end); void repeatNever(); void repeatDaily(); void checkValidity(); private: - void accept(); + void accept() override; DvbManager *manager; DvbSharedRecording recording; QLineEdit *nameEdit; QComboBox *channelBox; DateTimeEdit *beginEdit; DurationEdit *durationEdit; DateTimeEdit *endEdit; QCheckBox *dayCheckBoxes[7]; QDialogButtonBox *buttonBox; }; #endif /* DVBRECORDINGDIALOG_P_H */ diff --git a/src/dvb/dvbscan.cpp b/src/dvb/dvbscan.cpp index c97925a..1a0d136 100644 --- a/src/dvb/dvbscan.cpp +++ b/src/dvb/dvbscan.cpp @@ -1,1382 +1,1382 @@ /* * dvbscan.cpp * * Copyright (C) 2008-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "../log.h" #include #include #include #include "dvbdevice.h" #include "dvbscan.h" #include "dvbsi.h" class DvbPatEntry { public: DvbPatEntry(int programNumber_, int pid_) : programNumber(programNumber_), pid(pid_) { } ~DvbPatEntry() { } int programNumber; int pid; }; Q_DECLARE_TYPEINFO(DvbPatEntry, Q_MOVABLE_TYPE); class DvbSdtEntry { public: DvbSdtEntry(int serviceId_, int networkId_, bool scrambled_) : serviceId(serviceId_), networkId(networkId_), scrambled(scrambled_) { } ~DvbSdtEntry() { } int serviceId; int networkId; bool scrambled; QString name; QString provider; }; class DvbScanFilter : public DvbSectionFilter, QObject { public: DvbScanFilter(DvbScan *scan_, bool useOtherNit_) : scan(scan_), pid(-1), useOtherNit(useOtherNit_) { } ~DvbScanFilter() { stopFilter(); } bool isActive() const { return (pid != -1); } bool startFilter(int pid_, DvbScan::FilterType type_); void stopFilter(); private: struct sectCheck { int id; QBitArray check; }; bool checkMultipleSection(const DvbStandardSection §ion); bool isFinished(); - void processSection(const char *data, int size); - void timerEvent(QTimerEvent *); + void processSection(const char *data, int size) override; + void timerEvent(QTimerEvent *) override; DvbScan *scan; int pid; DvbScan::FilterType type; QVector multipleSections; int timerId; bool useOtherNit; }; bool DvbScanFilter::startFilter(int pid_, DvbScan::FilterType type_) { Q_ASSERT(pid == -1); pid = pid_; type = type_; multipleSections.clear(); if (!scan->device->addSectionFilter(pid, this)) { pid = -1; return false; } // FIXME check timings if (type != DvbScan::NitFilter) { timerId = startTimer(5000); } else { timerId = startTimer(20000); } return true; } void DvbScanFilter::stopFilter() { if (pid != -1) { killTimer(timerId); scan->device->removeSectionFilter(pid, this); multipleSections.clear(); pid = -1; } } bool DvbScanFilter::checkMultipleSection(const DvbStandardSection §ion) { int sectionCount = section.lastSectionNumber() + 1; int tableNumber = -1; int id = section.tableId() << 16 | section.tableIdExtension(); for (int i = 0; i < multipleSections.size(); i++) { if (multipleSections.at(i).id == id) { tableNumber = i; break; } } if (tableNumber < 0) { tableNumber = multipleSections.size(); multipleSections.resize(tableNumber + 1); multipleSections[tableNumber].id = id; multipleSections[tableNumber].check.resize(sectionCount); } if (section.sectionNumber() >= sectionCount) { qCWarning(logDvb, "Current section is bigger than the last one"); sectionCount = section.sectionNumber() + 1; } QBitArray *check = &multipleSections[tableNumber].check; if (check->isEmpty()) { check->resize(sectionCount); } else { if (check->size() != sectionCount) { qCWarning(logDvb, "Inconsistent number of sections"); if (check->size() < sectionCount) check->resize(sectionCount); } } if (check->testBit(section.sectionNumber())) { return false; } check->setBit(section.sectionNumber()); return true; } bool DvbScanFilter::isFinished() { for (int i = 0; i < multipleSections.size(); i++) { if (multipleSections[i].check.count(false) != 0) return false; } return true; } void DvbScanFilter::processSection(const char *data, int size) { switch (type) { case DvbScan::PatFilter: { DvbPatSection patSection(data, size); if (!patSection.isValid() || (patSection.tableId() != 0x0)) { return; } if (!checkMultipleSection(patSection)) { // already read this part return; } scan->processPat(patSection); break; } case DvbScan::PmtFilter: { DvbPmtSection pmtSection(data, size); if (!pmtSection.isValid() || (pmtSection.tableId() != 0x2)) { return; } if (!checkMultipleSection(pmtSection)) { // already read this part return; } scan->processPmt(pmtSection, pid); break; } case DvbScan::SdtFilter: { // FIXME: should we also handle other SDT table? DvbSdtSection sdtSection(data, size); if (!sdtSection.isValid() || (sdtSection.tableId() != 0x42)) { // there are also other tables in the SDT return; } if (!checkMultipleSection(sdtSection)) { // already read this part return; } scan->processSdt(sdtSection); break; } case DvbScan::VctFilter: { AtscVctSection vctSection(data, size); if (!vctSection.isValid() || ((vctSection.tableId() != 0xc8) && (vctSection.tableId() != 0xc9))) { // there are also other tables in the VCT return; } if (!checkMultipleSection(vctSection)) { // already read this part return; } scan->processVct(vctSection); break; } case DvbScan::NitFilter: { DvbNitSection nitSection(data, size); if (!nitSection.isValid()) return; if (!((nitSection.tableId() == 0x40) || (useOtherNit && (nitSection.tableId() == 0x41)))) return; qCDebug(logDvb, "Handling NIT table ID 0x%02x, extension 0x%04x", nitSection.tableId(), nitSection.tableIdExtension()); if (!checkMultipleSection(nitSection)) { // already read this part return; } scan->processNit(nitSection); break; } } if (isFinished()) scan->filterFinished(this); } void DvbScanFilter::timerEvent(QTimerEvent *) { qCWarning(logDvb, "Timeout while reading section; type = %d, PID = %d", type, pid); scan->filterFinished(this); } DvbScan::DvbScan(DvbDevice *device_, const QString &source_, const DvbTransponder &transponder_, bool useOtherNit_) : device(device_), source(source_), transponder(transponder_), isLive(true), isAuto(false), useOtherNit(useOtherNit_), transponderIndex(-1), state(ScanPat), patIndex(0), activeFilters(0) { qCDebug(logDvb, "Use other NIT is %s", useOtherNit ? "enabled" : "disabled"); } DvbScan::DvbScan(DvbDevice *device_, const QString &source_, const QList &transponders_, bool useOtherNit_) : device(device_), source(source_), isLive(false), isAuto(false), useOtherNit(useOtherNit_), transponders(transponders_), transponderIndex(0), state(ScanTune), patIndex(0), activeFilters(0) { qCDebug(logDvb, "Use other NIT is %s", useOtherNit ? "enabled" : "disabled"); } DvbScan::DvbScan(DvbDevice *device_, const QString &source_, const QString &autoScanSource, bool useOtherNit_) : device(device_), source(source_), isLive(false), isAuto(true), useOtherNit(useOtherNit_), transponderIndex(0), state(ScanTune), patIndex(0), activeFilters(0) { qCDebug(logDvb, "Use other NIT is %s", useOtherNit ? "enabled" : "disabled"); // Seek for DVB-T transponders if ((autoScanSource == QLatin1String("AUTO-T-Normal")) || (autoScanSource == QLatin1String("AUTO-T-Offsets")) || (autoScanSource == QLatin1String("AUTO-T2-Normal")) || (autoScanSource == QLatin1String("AUTO-T2-Offsets"))) { bool offsets = (autoScanSource == QLatin1String("AUTO-T-Offsets")) || (autoScanSource == QLatin1String("AUTO-T2-Offsets")); for (int frequency = 177500000; frequency <= 226500000; frequency += 7000000) { DvbTransponder currentTransponder(DvbTransponderBase::DvbT); DvbTTransponder *dvbTTransponder = currentTransponder.as(); dvbTTransponder->frequency = frequency; dvbTTransponder->bandwidth = DvbTTransponder::Bandwidth7MHz; dvbTTransponder->modulation = DvbTTransponder::ModulationAuto; dvbTTransponder->fecRateHigh = DvbTTransponder::FecAuto; dvbTTransponder->fecRateLow = DvbTTransponder::FecNone; dvbTTransponder->transmissionMode = DvbTTransponder::TransmissionModeAuto; dvbTTransponder->guardInterval = DvbTTransponder::GuardIntervalAuto; dvbTTransponder->hierarchy = DvbTTransponder::HierarchyNone; transponders.append(currentTransponder); } for (int frequency = 474000000; frequency <= 858000000; frequency += 8000000) { for (int i = 0; i < 3; ++i) { if ((i != 0) && (!offsets)) { break; } int offset = 0; if (i == 1) { offset = -167000; } else if (i == 2) { offset = 167000; } DvbTransponder currentTransponder(DvbTransponderBase::DvbT); DvbTTransponder *dvbTTransponder = currentTransponder.as(); dvbTTransponder->frequency = frequency + offset; dvbTTransponder->bandwidth = DvbTTransponder::Bandwidth8MHz; dvbTTransponder->modulation = DvbTTransponder::ModulationAuto; dvbTTransponder->fecRateHigh = DvbTTransponder::FecAuto; dvbTTransponder->fecRateLow = DvbTTransponder::FecNone; dvbTTransponder->transmissionMode = DvbTTransponder::TransmissionModeAuto; dvbTTransponder->guardInterval = DvbTTransponder::GuardIntervalAuto; dvbTTransponder->hierarchy = DvbTTransponder::HierarchyNone; transponders.append(currentTransponder); } } } else if ((autoScanSource == QLatin1String("AUTO-T-Australia")) || (autoScanSource == QLatin1String("AUTO-T2-Australia"))) { for (int frequency = 177500000; frequency <= 226500000; frequency += 7000000) { for (int i = 0; i < 2; ++i) { int offset = 0; if (i == 1) { offset = 125000; } DvbTransponder currentTransponder(DvbTransponderBase::DvbT); DvbTTransponder *dvbTTransponder = currentTransponder.as(); dvbTTransponder->frequency = frequency + offset; dvbTTransponder->bandwidth = DvbTTransponder::Bandwidth7MHz; dvbTTransponder->modulation = DvbTTransponder::ModulationAuto; dvbTTransponder->fecRateHigh = DvbTTransponder::FecAuto; dvbTTransponder->fecRateLow = DvbTTransponder::FecNone; dvbTTransponder->transmissionMode = DvbTTransponder::TransmissionModeAuto; dvbTTransponder->guardInterval = DvbTTransponder::GuardIntervalAuto; dvbTTransponder->hierarchy = DvbTTransponder::HierarchyNone; transponders.append(currentTransponder); } } for (int frequency = 529500000; frequency <= 816500000; frequency += 7000000) { for (int i = 0; i < 2; ++i) { int offset = 0; if (i == 1) { offset = 125000; } DvbTransponder currentTransponder(DvbTransponderBase::DvbT); DvbTTransponder *dvbTTransponder = currentTransponder.as(); dvbTTransponder->frequency = frequency + offset; dvbTTransponder->bandwidth = DvbTTransponder::Bandwidth7MHz; dvbTTransponder->modulation = DvbTTransponder::ModulationAuto; dvbTTransponder->fecRateHigh = DvbTTransponder::FecAuto; dvbTTransponder->fecRateLow = DvbTTransponder::FecNone; dvbTTransponder->transmissionMode = DvbTTransponder::TransmissionModeAuto; dvbTTransponder->guardInterval = DvbTTransponder::GuardIntervalAuto; dvbTTransponder->hierarchy = DvbTTransponder::HierarchyNone; transponders.append(currentTransponder); } } } else if ((autoScanSource == QLatin1String("AUTO-T-Italy")) || (autoScanSource == QLatin1String("AUTO-T2-Italy"))) { static const int italyVhf[] = { 177500000, 186000000, 194500000, 203500000, 212500000, 219500000, 226500000 }; for (unsigned i = 0; i < (sizeof(italyVhf) / sizeof(italyVhf[0])); ++i) { for (int j = 0; j < 2; ++j) { DvbTransponder currentTransponder(DvbTransponderBase::DvbT); DvbTTransponder *dvbTTransponder = currentTransponder.as(); dvbTTransponder->frequency = italyVhf[i]; dvbTTransponder->bandwidth = ((j == 0) ? DvbTTransponder::Bandwidth7MHz : DvbTTransponder::Bandwidth8MHz); dvbTTransponder->modulation = DvbTTransponder::ModulationAuto; dvbTTransponder->fecRateHigh = DvbTTransponder::FecAuto; dvbTTransponder->fecRateLow = DvbTTransponder::FecNone; dvbTTransponder->transmissionMode = DvbTTransponder::TransmissionModeAuto; dvbTTransponder->guardInterval = DvbTTransponder::GuardIntervalAuto; dvbTTransponder->hierarchy = DvbTTransponder::HierarchyNone; transponders.append(currentTransponder); } } for (int frequency = 474000000; frequency <= 858000000; frequency += 8000000) { DvbTransponder currentTransponder(DvbTransponderBase::DvbT); DvbTTransponder *dvbTTransponder = currentTransponder.as(); dvbTTransponder->frequency = frequency; dvbTTransponder->bandwidth = DvbTTransponder::Bandwidth8MHz; dvbTTransponder->modulation = DvbTTransponder::ModulationAuto; dvbTTransponder->fecRateHigh = DvbTTransponder::FecAuto; dvbTTransponder->fecRateLow = DvbTTransponder::FecNone; dvbTTransponder->transmissionMode = DvbTTransponder::TransmissionModeAuto; dvbTTransponder->guardInterval = DvbTTransponder::GuardIntervalAuto; dvbTTransponder->hierarchy = DvbTTransponder::HierarchyNone; transponders.append(currentTransponder); } } else if ((autoScanSource == QLatin1String("AUTO-T-Taiwan"))|| (autoScanSource == QLatin1String("AUTO-T2-Taiwan"))) { for (int frequency = 527000000; frequency <= 599000000; frequency += 6000000) { DvbTransponder currentTransponder(DvbTransponderBase::DvbT); DvbTTransponder *dvbTTransponder = currentTransponder.as(); dvbTTransponder->frequency = frequency; dvbTTransponder->bandwidth = DvbTTransponder::Bandwidth6MHz; dvbTTransponder->modulation = DvbTTransponder::ModulationAuto; dvbTTransponder->fecRateHigh = DvbTTransponder::FecAuto; dvbTTransponder->fecRateLow = DvbTTransponder::FecNone; dvbTTransponder->transmissionMode = DvbTTransponder::TransmissionModeAuto; dvbTTransponder->guardInterval = DvbTTransponder::GuardIntervalAuto; dvbTTransponder->hierarchy = DvbTTransponder::HierarchyNone; transponders.append(currentTransponder); } } // Seek for DVB-T2 transponders if ((autoScanSource == QLatin1String("AUTO-T2-Normal")) || (autoScanSource == QLatin1String("AUTO-T2-Offsets"))) { bool offsets = (autoScanSource == QLatin1String("AUTO-T2-Offsets")); for (int frequency = 177500000; frequency <= 226500000; frequency += 7000000) { DvbTransponder currentTransponder(DvbTransponderBase::DvbT2); DvbT2Transponder *dvbT2Transponder = currentTransponder.as(); dvbT2Transponder->frequency = frequency; dvbT2Transponder->bandwidth = DvbT2Transponder::Bandwidth7MHz; dvbT2Transponder->modulation = DvbT2Transponder::ModulationAuto; dvbT2Transponder->fecRateHigh = DvbT2Transponder::FecAuto; dvbT2Transponder->fecRateLow = DvbT2Transponder::FecNone; dvbT2Transponder->transmissionMode = DvbT2Transponder::TransmissionModeAuto; dvbT2Transponder->guardInterval = DvbT2Transponder::GuardIntervalAuto; dvbT2Transponder->hierarchy = DvbT2Transponder::HierarchyNone; dvbT2Transponder->streamId = 0; transponders.append(currentTransponder); } for (int frequency = 474000000; frequency <= 858000000; frequency += 8000000) { for (int i = 0; i < 3; ++i) { if ((i != 0) && (!offsets)) { break; } int offset = 0; if (i == 1) { offset = -167000; } else if (i == 2) { offset = 167000; } DvbTransponder currentTransponder(DvbTransponderBase::DvbT2); DvbT2Transponder *dvbT2Transponder = currentTransponder.as(); dvbT2Transponder->frequency = frequency + offset; dvbT2Transponder->bandwidth = DvbT2Transponder::Bandwidth8MHz; dvbT2Transponder->modulation = DvbT2Transponder::ModulationAuto; dvbT2Transponder->fecRateHigh = DvbT2Transponder::FecAuto; dvbT2Transponder->fecRateLow = DvbT2Transponder::FecNone; dvbT2Transponder->transmissionMode = DvbT2Transponder::TransmissionModeAuto; dvbT2Transponder->guardInterval = DvbT2Transponder::GuardIntervalAuto; dvbT2Transponder->hierarchy = DvbT2Transponder::HierarchyNone; dvbT2Transponder->streamId = 0; transponders.append(currentTransponder); } } } else if (autoScanSource == QLatin1String("AUTO-T2-Australia")) { for (int frequency = 177500000; frequency <= 226500000; frequency += 7000000) { for (int i = 0; i < 2; ++i) { int offset = 0; if (i == 1) { offset = 125000; } DvbTransponder currentTransponder(DvbTransponderBase::DvbT2); DvbT2Transponder *dvbT2Transponder = currentTransponder.as(); dvbT2Transponder->frequency = frequency + offset; dvbT2Transponder->bandwidth = DvbT2Transponder::Bandwidth7MHz; dvbT2Transponder->modulation = DvbT2Transponder::ModulationAuto; dvbT2Transponder->fecRateHigh = DvbT2Transponder::FecAuto; dvbT2Transponder->fecRateLow = DvbT2Transponder::FecNone; dvbT2Transponder->transmissionMode = DvbT2Transponder::TransmissionModeAuto; dvbT2Transponder->guardInterval = DvbT2Transponder::GuardIntervalAuto; dvbT2Transponder->hierarchy = DvbT2Transponder::HierarchyNone; dvbT2Transponder->streamId = 0; transponders.append(currentTransponder); } } for (int frequency = 529500000; frequency <= 816500000; frequency += 7000000) { for (int i = 0; i < 2; ++i) { int offset = 0; if (i == 1) { offset = 125000; } DvbTransponder currentTransponder(DvbTransponderBase::DvbT2); DvbT2Transponder *dvbT2Transponder = currentTransponder.as(); dvbT2Transponder->frequency = frequency + offset; dvbT2Transponder->bandwidth = DvbT2Transponder::Bandwidth7MHz; dvbT2Transponder->modulation = DvbT2Transponder::ModulationAuto; dvbT2Transponder->fecRateHigh = DvbT2Transponder::FecAuto; dvbT2Transponder->fecRateLow = DvbT2Transponder::FecNone; dvbT2Transponder->transmissionMode = DvbT2Transponder::TransmissionModeAuto; dvbT2Transponder->guardInterval = DvbT2Transponder::GuardIntervalAuto; dvbT2Transponder->hierarchy = DvbT2Transponder::HierarchyNone; dvbT2Transponder->streamId = 0; transponders.append(currentTransponder); } } } else if (autoScanSource == QLatin1String("AUTO-T2-Italy")) { static const int italyVhf[] = { 177500000, 186000000, 194500000, 203500000, 212500000, 219500000, 226500000 }; for (unsigned i = 0; i < (sizeof(italyVhf) / sizeof(italyVhf[0])); ++i) { for (int j = 0; j < 2; ++j) { DvbTransponder currentTransponder(DvbTransponderBase::DvbT2); DvbT2Transponder *dvbT2Transponder = currentTransponder.as(); dvbT2Transponder->frequency = italyVhf[i]; dvbT2Transponder->bandwidth = ((j == 0) ? DvbT2Transponder::Bandwidth7MHz : DvbT2Transponder::Bandwidth8MHz); dvbT2Transponder->modulation = DvbT2Transponder::ModulationAuto; dvbT2Transponder->fecRateHigh = DvbT2Transponder::FecAuto; dvbT2Transponder->fecRateLow = DvbT2Transponder::FecNone; dvbT2Transponder->transmissionMode = DvbT2Transponder::TransmissionModeAuto; dvbT2Transponder->guardInterval = DvbT2Transponder::GuardIntervalAuto; dvbT2Transponder->hierarchy = DvbT2Transponder::HierarchyNone; dvbT2Transponder->streamId = 0; transponders.append(currentTransponder); } } for (int frequency = 474000000; frequency <= 858000000; frequency += 8000000) { DvbTransponder currentTransponder(DvbTransponderBase::DvbT2); DvbT2Transponder *dvbT2Transponder = currentTransponder.as(); dvbT2Transponder->frequency = frequency; dvbT2Transponder->bandwidth = DvbT2Transponder::Bandwidth8MHz; dvbT2Transponder->modulation = DvbT2Transponder::ModulationAuto; dvbT2Transponder->fecRateHigh = DvbT2Transponder::FecAuto; dvbT2Transponder->fecRateLow = DvbT2Transponder::FecNone; dvbT2Transponder->transmissionMode = DvbT2Transponder::TransmissionModeAuto; dvbT2Transponder->guardInterval = DvbT2Transponder::GuardIntervalAuto; dvbT2Transponder->hierarchy = DvbT2Transponder::HierarchyNone; dvbT2Transponder->streamId = 0; transponders.append(currentTransponder); } } else if (autoScanSource == QLatin1String("AUTO-T2-Taiwan")) { for (int frequency = 527000000; frequency <= 599000000; frequency += 6000000) { DvbTransponder currentTransponder(DvbTransponderBase::DvbT2); DvbT2Transponder *dvbT2Transponder = currentTransponder.as(); dvbT2Transponder->frequency = frequency; dvbT2Transponder->bandwidth = DvbT2Transponder::Bandwidth6MHz; dvbT2Transponder->modulation = DvbT2Transponder::ModulationAuto; dvbT2Transponder->fecRateHigh = DvbT2Transponder::FecAuto; dvbT2Transponder->fecRateLow = DvbT2Transponder::FecNone; dvbT2Transponder->transmissionMode = DvbT2Transponder::TransmissionModeAuto; dvbT2Transponder->guardInterval = DvbT2Transponder::GuardIntervalAuto; dvbT2Transponder->hierarchy = DvbT2Transponder::HierarchyNone; dvbT2Transponder->streamId = 0; transponders.append(currentTransponder); } } // Seek for ISDB-T transponders if (autoScanSource == QLatin1String("AUTO-UHF-6MHz")) { for (int frequency = 473142857; frequency <= 803142857; frequency += 6000000) { DvbTransponder currentTransponder(DvbTransponderBase::IsdbT); IsdbTTransponder *isdbTTransponder = currentTransponder.as(); isdbTTransponder->frequency = frequency; isdbTTransponder->bandwidth = IsdbTTransponder::Bandwidth6MHz; isdbTTransponder->transmissionMode = IsdbTTransponder::TransmissionModeAuto; isdbTTransponder->guardInterval = IsdbTTransponder::GuardIntervalAuto; isdbTTransponder->partialReception = IsdbTTransponder::PR_AUTO; isdbTTransponder->soundBroadcasting = IsdbTTransponder::SB_disabled; for (int i = 0; i < 3; i++) { isdbTTransponder->layerEnabled[i] = true; isdbTTransponder->modulation[i] = IsdbTTransponder::ModulationAuto; isdbTTransponder->fecRate[i] = IsdbTTransponder::FecAuto; isdbTTransponder->interleaving[i] = IsdbTTransponder::I_AUTO; isdbTTransponder->segmentCount[i] = 15; } transponders.append(currentTransponder); } } } DvbScan::~DvbScan() { qDeleteAll(filters); } void DvbScan::start() { connect(device, SIGNAL(stateChanged()), this, SLOT(deviceStateChanged())); updateState(); } void DvbScan::deviceStateChanged() { if (device->getDeviceState() == DvbDevice::DeviceReleased) { qCWarning(logDvb, "Device was released. Stopping scan"); emit scanFinished(); return; } if (state == ScanTuning) { updateState(); } } bool DvbScan::startFilter(int pid, FilterType type) { if (activeFilters != filters.size()) { foreach (DvbScanFilter *filter, filters) { if (!filter->isActive()) { if (!filter->startFilter(pid, type)) { return false; } ++activeFilters; return true; } } Q_ASSERT(false); } else if (activeFilters < 10) { DvbScanFilter *filter = new DvbScanFilter(this, useOtherNit); if (!filter->startFilter(pid, type)) { delete filter; return false; } filters.append(filter); ++activeFilters; return true; } return false; } void DvbScan::updateState() { while (true) { switch (state) { case ScanPat: { if (!startFilter(0x0, PatFilter)) { return; } snr = device->getSnr(scale); state = ScanNit; } // fall through case ScanNit: { if (!isLive && !isAuto && (transponder.getTransmissionType() != DvbTransponderBase::Atsc)) { if (!startFilter(0x10, NitFilter)) { return; } } state = ScanSdt; } // fall through case ScanSdt: { if (transponder.getTransmissionType() != DvbTransponderBase::Atsc) { if (!startFilter(0x11, SdtFilter)) { return; } } else { if (!startFilter(0x1ffb, VctFilter)) { return; } } state = ScanPmt; } // fall through case ScanPmt: { while (patIndex < patEntries.size()) { if (!startFilter(patEntries.at(patIndex).pid, PmtFilter)) { return; } ++patIndex; } if (activeFilters != 0) { return; } for (int i = 0; i < channels.size(); ++i) { DvbPreviewChannel &channel = channels[i]; foreach (const DvbSdtEntry &sdtEntry, sdtEntries) { if (channel.serviceId == sdtEntry.serviceId) { channel.name = sdtEntry.name; channel.networkId = sdtEntry.networkId; channel.isScrambled = sdtEntry.scrambled; channel.provider = sdtEntry.provider; break; } } if (channel.name.isEmpty()) { channel.name = QString(QLatin1String("#0 %1:%2")). arg(channel.transportStreamId). arg(channel.serviceId); } qCDebug(logDvb, "Found channel %s", qPrintable(channel.name)); } if (!channels.isEmpty()) { emit foundChannels(channels); } if (isLive) { qCInfo(logDvb, "Scanning while live stream. Can't change the transponder"); emit scanFinished(); return; } patEntries.clear(); patIndex = 0; sdtEntries.clear(); channels.clear(); state = ScanTune; } // fall through case ScanTune: { if (transponders.size() > 0) { emit scanProgress((100 * transponderIndex) / transponders.size()); } qCDebug(logDvb, "Transponder %d/%d", transponderIndex, transponders.size()); if (transponderIndex >= transponders.size()) { emit scanFinished(); return; } transponder = transponders.at(transponderIndex); ++transponderIndex; state = ScanTuning; if (!isAuto) { device->tune(transponder); } else { device->autoTune(transponder); } return; } case ScanTuning: { switch (device->getDeviceState()) { case DvbDevice::DeviceIdle: state = ScanTune; break; case DvbDevice::DeviceTuned: if (isAuto) { transponders[transponderIndex - 1] = device->getAutoTransponder(); } state = ScanPat; break; default: return; } break; } } } } void DvbScan::processPat(const DvbPatSection §ion) { transportStreamId = section.transportStreamId(); for (DvbPatSectionEntry entry = section.entries(); entry.isValid(); entry.advance()) { if (entry.programNumber() != 0x0) { // skip 0x0 which has a special meaning patEntries.append(DvbPatEntry(entry.programNumber(), entry.pid())); qCDebug(logDvb, "New PAT entry: pid %d, program %d", entry.pid(), entry.programNumber()); } } } void DvbScan::processPmt(const DvbPmtSection §ion, int pid) { DvbPreviewChannel channel; DvbPmtParser parser(section); channel.hasVideo = (parser.videoPid >= 0); if (!parser.audioPids.isEmpty()) { channel.audioPid = parser.audioPids.at(0).first; } if (channel.hasVideo || (channel.audioPid >= 0)) { channel.source = source; channel.transponder = transponder; channel.transportStreamId = transportStreamId; channel.pmtPid = pid; channel.pmtSectionData = section.toByteArray(); channel.serviceId = section.programNumber(); switch (scale) { case DvbBackendDevice::dBuV: case DvbBackendDevice::NotSupported: { channel.snr = ""; break; } case DvbBackendDevice::Percentage: { channel.snr = QString::number(snr, 'f', 0) + '%'; break; } case DvbBackendDevice::Decibel: { channel.snr = QString::number(snr, 'f', 2) + " dB"; break; } }; channels.append(channel); qCDebug(logDvb, "New channel: PID %d, service ID %d", pid, section.programNumber()); } } void DvbScan::processSdt(const DvbSdtSection §ion) { for (DvbSdtSectionEntry entry = section.entries(); entry.isValid(); entry.advance()) { DvbSdtEntry sdtEntry(entry.serviceId(), section.originalNetworkId(), entry.isScrambled()); for (DvbDescriptor descriptor = entry.descriptors(); descriptor.isValid(); descriptor.advance()) { if (descriptor.descriptorTag() != 0x48) { continue; } DvbServiceDescriptor serviceDescriptor(descriptor); if (!serviceDescriptor.isValid()) { continue; } sdtEntry.name = serviceDescriptor.serviceName(); sdtEntry.provider = serviceDescriptor.providerName(); qCDebug(logDvb, "New SDT entry: service ID 0x%04x, name '%s', provider '%s'", entry.serviceId(), qPrintable(sdtEntry.name), qPrintable(sdtEntry.provider)); sdtEntries.append(sdtEntry); } } } void DvbScan::processVct(const AtscVctSection §ion) { AtscVctSectionEntry entry = section.entries(); int entryCount = section.entryCount(); for (int i = 0; i < entryCount && (entry.isValid()); i++) { QString majorminor = QString(QLatin1String("%1-%2 ")). arg(entry.majorNumber(), 3, 10, QLatin1Char('0')).arg(entry.minorNumber()); DvbSdtEntry sdtEntry(entry.programNumber(), entry.sourceId(), entry.isScrambled()); // Each VCT section has it's own list of descriptors // See A/65C table 6.25a for the list of descriptors for (DvbDescriptor descriptor = entry.descriptors(); descriptor.isValid(); descriptor.advance()) { if (descriptor.descriptorTag() == 0xa0) { // Extended Channel Name Descriptor AtscChannelNameDescriptor nameDescriptor(descriptor); if (!nameDescriptor.isValid()) { continue; } sdtEntry.name = majorminor + nameDescriptor.name(); } } if (sdtEntry.name.isEmpty()) { // Extended Channel name not available, fall back // to the short name QChar shortName[] = { entry.shortName1(), entry.shortName2(), entry.shortName3(), entry.shortName4(), entry.shortName5(), entry.shortName6(), entry.shortName7(), 0 }; int nameLength = 0; while (shortName[nameLength] != 0) { ++nameLength; } sdtEntry.name = majorminor + QString(shortName, nameLength); } qCDebug(logDvb, "New SDT entry: name %s", qPrintable(sdtEntry.name)); sdtEntries.append(sdtEntry); if (i < entryCount - 1) entry.advance(); } } void DvbScan::processNit(const DvbNitSection §ion) { for (DvbNitSectionEntry entry = section.entries(); entry.isValid(); entry.advance()) { for (DvbDescriptor descriptor = entry.descriptors(); descriptor.isValid(); descriptor.advance()) { processNitDescriptor(descriptor); } } } static DvbCTransponder::Modulation extractDvbCModulation(const DvbCableDescriptor &descriptor) { switch (descriptor.modulation()) { case 1: return DvbCTransponder::Qam16; case 2: return DvbCTransponder::Qam32; case 3: return DvbCTransponder::Qam64; case 4: return DvbCTransponder::Qam128; case 5: return DvbCTransponder::Qam256; default: return DvbCTransponder::ModulationAuto; } } static DvbCTransponder::FecRate extractDvbCFecRate(const DvbCableDescriptor &descriptor) { switch (descriptor.fecRate()) { case 1: return DvbTransponderBase::Fec1_2; case 2: return DvbTransponderBase::Fec2_3; case 3: return DvbTransponderBase::Fec3_4; case 4: return DvbTransponderBase::Fec5_6; case 5: return DvbTransponderBase::Fec7_8; case 6: return DvbTransponderBase::Fec8_9; case 7: return DvbTransponderBase::Fec3_5; case 8: return DvbTransponderBase::Fec4_5; case 9: return DvbTransponderBase::Fec9_10; case 15: return DvbTransponderBase::FecNone; default: return DvbTransponderBase::FecAuto; } } static DvbSTransponder::Polarization extractDvbSPolarization( const DvbSatelliteDescriptor &descriptor) { switch (descriptor.polarization()) { case 0: return DvbSTransponder::Horizontal; case 1: return DvbSTransponder::Vertical; case 2: return DvbSTransponder::CircularLeft; default: return DvbSTransponder::CircularRight; } } static DvbSTransponder::FecRate extractDvbSFecRate(const DvbSatelliteDescriptor &descriptor) { switch (descriptor.fecRate()) { case 1: return DvbTransponderBase::Fec1_2; case 2: return DvbTransponderBase::Fec2_3; case 3: return DvbTransponderBase::Fec3_4; case 4: return DvbTransponderBase::Fec5_6; case 5: return DvbTransponderBase::Fec7_8; case 6: return DvbTransponderBase::Fec8_9; case 7: return DvbTransponderBase::Fec3_5; case 8: return DvbTransponderBase::Fec4_5; case 9: return DvbTransponderBase::Fec9_10; default: return DvbTransponderBase::FecAuto; } } static DvbS2Transponder::Modulation extractDvbS2Modulation( const DvbSatelliteDescriptor &descriptor) { switch (descriptor.modulation()) { case 1: return DvbS2Transponder::Qpsk; case 2: return DvbS2Transponder::Psk8; default: return DvbS2Transponder::ModulationAuto; } } static DvbS2Transponder::RollOff extractDvbS2RollOff(const DvbSatelliteDescriptor &descriptor) { switch (descriptor.rollOff()) { case 0: return DvbS2Transponder::RollOff35; case 1: return DvbS2Transponder::RollOff25; case 2: return DvbS2Transponder::RollOff20; default: return DvbS2Transponder::RollOffAuto; } } static DvbTTransponder::Bandwidth extractDvbTBandwidth(const DvbTerrestrialDescriptor &descriptor) { switch (descriptor.bandwidth()) { case 0: return DvbTTransponder::Bandwidth8MHz; case 1: return DvbTTransponder::Bandwidth7MHz; case 2: return DvbTTransponder::Bandwidth6MHz; case 3: return DvbTTransponder::Bandwidth5MHz; default: return DvbTTransponder::BandwidthAuto; } } static DvbTTransponder::Modulation extractDvbTModulation( const DvbTerrestrialDescriptor &descriptor) { switch (descriptor.constellation()) { case 0: return DvbTTransponder::Qpsk; case 1: return DvbTTransponder::Qam16; case 2: return DvbTTransponder::Qam64; default: return DvbTTransponder::ModulationAuto; } } static DvbTTransponder::FecRate extractDvbTFecRateHigh(const DvbTerrestrialDescriptor &descriptor) { switch (descriptor.fecRateHigh()) { case 0: return DvbTTransponder::Fec1_2; case 1: return DvbTTransponder::Fec2_3; case 2: return DvbTTransponder::Fec3_4; case 3: return DvbTTransponder::Fec5_6; case 4: return DvbTTransponder::Fec7_8; default: return DvbTTransponder::FecAuto; } } static DvbTTransponder::FecRate extractDvbTFecRateLow(const DvbTerrestrialDescriptor &descriptor) { switch (descriptor.fecRateLow()) { case 0: return DvbTTransponder::Fec1_2; case 1: return DvbTTransponder::Fec2_3; case 2: return DvbTTransponder::Fec3_4; case 3: return DvbTTransponder::Fec5_6; case 4: return DvbTTransponder::Fec7_8; default: return DvbTTransponder::FecAuto; } } static DvbTTransponder::TransmissionMode extractDvbTTransmissionMode( const DvbTerrestrialDescriptor &descriptor) { switch (descriptor.transmissionMode()) { case 0: return DvbTTransponder::TransmissionMode2k; case 1: return DvbTTransponder::TransmissionMode8k; case 2: return DvbTTransponder::TransmissionMode4k; default: return DvbTTransponder::TransmissionModeAuto; } } static DvbTTransponder::GuardInterval extractDvbTGuardInterval( const DvbTerrestrialDescriptor &descriptor) { switch (descriptor.guardInterval()) { case 0: return DvbTTransponder::GuardInterval1_32; case 1: return DvbTTransponder::GuardInterval1_16; case 2: return DvbTTransponder::GuardInterval1_8; default: return DvbTTransponder::GuardInterval1_4; } } static DvbTTransponder::Hierarchy extractDvbTHierarchy(const DvbTerrestrialDescriptor &descriptor) { switch (descriptor.hierarchy() & 0x3) { case 0: return DvbTTransponder::HierarchyNone; case 1: return DvbTTransponder::Hierarchy1; case 2: return DvbTTransponder::Hierarchy2; default: return DvbTTransponder::Hierarchy4; } } // FIXME: Implement DvbT2Descriptor void DvbScan::processNitDescriptor(const DvbDescriptor &descriptor) { DvbTransponder newTransponder(transponder.getTransmissionType()); switch (transponder.getTransmissionType()) { case DvbTransponderBase::Invalid: qCWarning(logDvb, "Invalid transponder type"); return; case DvbTransponderBase::DvbC: { if (descriptor.descriptorTag() != 0x44) { return; } DvbCableDescriptor cableDescriptor(descriptor); if (!cableDescriptor.isValid()) { return; } newTransponder = DvbTransponder(DvbTransponderBase::DvbC); DvbCTransponder *dvbCTransponder = newTransponder.as(); dvbCTransponder->frequency = DvbDescriptor::bcdToInt(cableDescriptor.frequency(), 100); dvbCTransponder->symbolRate = DvbDescriptor::bcdToInt(cableDescriptor.symbolRate(), 100); dvbCTransponder->modulation = extractDvbCModulation(cableDescriptor); dvbCTransponder->fecRate = extractDvbCFecRate(cableDescriptor); qCDebug(logDvb, "Added transponder: %.2f MHz", dvbCTransponder->frequency / 1000000.); break; } case DvbTransponderBase::DvbS: case DvbTransponderBase::DvbS2: { if (descriptor.descriptorTag() != 0x43) { return; } DvbSatelliteDescriptor satelliteDescriptor(descriptor); if (!satelliteDescriptor.isValid()) { return; } DvbSTransponder *dvbSTransponder; if (!satelliteDescriptor.isDvbS2()) { newTransponder = DvbTransponder(DvbTransponderBase::DvbS); dvbSTransponder = newTransponder.as(); } else { if ((device->getTransmissionTypes() & DvbDevice::DvbS2) == 0) { return; } newTransponder = DvbTransponder(DvbTransponderBase::DvbS2); DvbS2Transponder *dvbS2Transponder = newTransponder.as(); dvbS2Transponder->modulation = extractDvbS2Modulation(satelliteDescriptor); dvbS2Transponder->rollOff = extractDvbS2RollOff(satelliteDescriptor); dvbSTransponder = dvbS2Transponder; } dvbSTransponder->frequency = DvbDescriptor::bcdToInt(satelliteDescriptor.frequency(), 10); dvbSTransponder->polarization = extractDvbSPolarization(satelliteDescriptor); dvbSTransponder->symbolRate = DvbDescriptor::bcdToInt(satelliteDescriptor.symbolRate(), 100); dvbSTransponder->fecRate = extractDvbSFecRate(satelliteDescriptor); qCDebug(logDvb, "Added transponder: %.2f MHz", dvbSTransponder->frequency / 1000000.); break; } case DvbTransponderBase::DvbT2: // FIXME: Implement T2_delivery_system_descriptor // descriptor 0x7f, extension descriptor 0x04 or use libdvbv5 case DvbTransponderBase::DvbT: { if (descriptor.descriptorTag() != 0x5a) { return; } DvbTerrestrialDescriptor terrestrialDescriptor(descriptor); if (!terrestrialDescriptor.isValid()) { return; } newTransponder = DvbTransponder(DvbTransponderBase::DvbT); DvbTTransponder *dvbTTransponder = newTransponder.as(); dvbTTransponder->frequency = terrestrialDescriptor.frequency() * 10; dvbTTransponder->bandwidth = extractDvbTBandwidth(terrestrialDescriptor); dvbTTransponder->modulation = extractDvbTModulation(terrestrialDescriptor); dvbTTransponder->fecRateHigh = extractDvbTFecRateHigh(terrestrialDescriptor); dvbTTransponder->fecRateLow = extractDvbTFecRateLow(terrestrialDescriptor); dvbTTransponder->transmissionMode = extractDvbTTransmissionMode(terrestrialDescriptor); dvbTTransponder->guardInterval = extractDvbTGuardInterval(terrestrialDescriptor); dvbTTransponder->hierarchy = extractDvbTHierarchy(terrestrialDescriptor); if (dvbTTransponder->hierarchy == DvbTTransponder::HierarchyNone) { dvbTTransponder->fecRateLow = DvbTTransponder::FecNone; } qCDebug(logDvb, "Added transponder: %.2f MHz", dvbTTransponder->frequency / 1000000.); break; } case DvbTransponderBase::Atsc: return; case DvbTransponderBase::IsdbT: if (descriptor.descriptorTag() != 0xfa) { return; } IsdbTerrestrialDescriptor IsdbTDescriptor(descriptor); if (!IsdbTDescriptor.isValid()) { return; } for (int i = 0; i < IsdbTDescriptor.frequencyLength(); i++) { newTransponder = DvbTransponder(DvbTransponderBase::IsdbT); IsdbTTransponder *isdbTTransponder = newTransponder.as(); isdbTTransponder->frequency = (uint32_t)((((uint64_t)IsdbTDescriptor.frequency(i)) * 1000000ul) / 7); isdbTTransponder->bandwidth = IsdbTTransponder::Bandwidth6MHz; isdbTTransponder->transmissionMode = IsdbTTransponder::TransmissionModeAuto; isdbTTransponder->guardInterval = IsdbTTransponder::GuardIntervalAuto; isdbTTransponder->partialReception = IsdbTTransponder::PR_AUTO; isdbTTransponder->soundBroadcasting = IsdbTTransponder::SB_disabled; for (int i = 0; i < 3; i++) { isdbTTransponder->layerEnabled[i] = true; isdbTTransponder->modulation[i] = IsdbTTransponder::ModulationAuto; isdbTTransponder->fecRate[i] = IsdbTTransponder::FecAuto; isdbTTransponder->interleaving[i] = IsdbTTransponder::I_AUTO; isdbTTransponder->segmentCount[i] = 15; } bool duplicate = false; foreach (const DvbTransponder &existingTransponder, transponders) { if (existingTransponder.corresponds(newTransponder)) { duplicate = true; break; } } if (duplicate) continue; transponders.append(newTransponder); qCDebug(logDvb, "Added transponder: %.2f MHz", isdbTTransponder->frequency / 1000000.); } return; } // New transponder was found. Add it foreach (const DvbTransponder &existingTransponder, transponders) { if (existingTransponder.corresponds(newTransponder)) return; } transponders.append(newTransponder); } void DvbScan::filterFinished(DvbScanFilter *filter) { filter->stopFilter(); --activeFilters; updateState(); } diff --git a/src/dvb/dvbscandialog.cpp b/src/dvb/dvbscandialog.cpp index ef2ca72..a5ee46e 100644 --- a/src/dvb/dvbscandialog.cpp +++ b/src/dvb/dvbscandialog.cpp @@ -1,611 +1,611 @@ /* * dvbscandialog.cpp * * Copyright (C) 2008-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dvbchanneldialog.h" #include "dvbdevice.h" #include "dvbliveview.h" #include "dvbmanager.h" #include "dvbscan.h" #include "dvbscandialog.h" DvbGradProgress::DvbGradProgress(QWidget *parent) : QLabel(parent), value(0), max(100.), min(0.) { setAlignment(Qt::AlignCenter); setFrameShape(Box); setText(""); } DvbGradProgress::~DvbGradProgress() { } void DvbGradProgress::setValue(float value_, DvbBackendDevice::Scale scale) { QString text; value = value_; switch(scale) { case DvbBackendDevice::NotSupported: { text = '-'; max = 100; min = 0; break; } case DvbBackendDevice::Percentage: { text = QString::number(value, 'f', 0) + '%'; max = 100; min = 0; break; } case DvbBackendDevice::Decibel: { text = QString::number(value, 'f', 2) + " dB"; max = 40; min = 0; break; } case DvbBackendDevice::dBuV: { text = QString::number(value, 'f', 2) + " dB" + QString((QChar) 0x00b5) + 'V'; max = 80; min = 20; break; } } setText(i18n("%1", text)); update(); } void DvbGradProgress::paintEvent(QPaintEvent *event) { QPainter painter(this); int border = frameWidth(); QRect rect(border, border, width() - 2 * border, height() - 2 * border); QLinearGradient gradient(rect.topLeft(), rect.topRight()); gradient.setColorAt(0, Qt::red); gradient.setColorAt(1, Qt::green); if (value < min) value = min; if (value > max) value = max; rect.setWidth((rect.width() * (value - min)) / (max - min)); painter.fillRect(rect, gradient); QLabel::paintEvent(event); } class DvbPreviewChannelTableModel : public QAbstractTableModel { public: explicit DvbPreviewChannelTableModel(QObject *parent) : QAbstractTableModel(parent) { } ~DvbPreviewChannelTableModel() { } enum ItemDataRole { DvbPreviewChannelRole = Qt::UserRole }; QAbstractItemModel *createProxyModel(QObject *parent); - int columnCount(const QModelIndex &parent) const; - int rowCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; + int columnCount(const QModelIndex &parent) const override; + int rowCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; void appendChannels(const QList &list) { beginInsertRows(QModelIndex(), channels.size(), channels.size() + list.size() - 1); channels += list; endInsertRows(); } const DvbPreviewChannel &getChannel(int pos) const { return channels.at(pos); } QList getChannels() const { return channels; } void removeChannels() { beginResetModel(); channels.clear(); endResetModel(); } private: QList channels; }; Q_DECLARE_METATYPE(const DvbPreviewChannel *) QAbstractItemModel *DvbPreviewChannelTableModel::createProxyModel(QObject *parent) { QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(parent); proxyModel->setDynamicSortFilter(true); proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); proxyModel->setSortLocaleAware(true); proxyModel->setSourceModel(this); return proxyModel; } int DvbPreviewChannelTableModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return 3; } int DvbPreviewChannelTableModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return channels.size(); } QVariant DvbPreviewChannelTableModel::data(const QModelIndex &index, int role) const { const DvbPreviewChannel &channel = channels.at(index.row()); switch (role) { case Qt::DecorationRole: if (index.column() == 0) { if (channel.hasVideo) { if (!channel.isScrambled) { return QIcon::fromTheme(QLatin1String("video-television"), QIcon(":video-television")); } else { return QIcon::fromTheme(QLatin1String("video-television-encrypted"), QIcon(":video-television-encrypted")); } } else { if (!channel.isScrambled) { return QIcon::fromTheme(QLatin1String("text-speak"), QIcon(":text-speak")); } else { return QIcon::fromTheme(QLatin1String("audio-radio-encrypted"), QIcon(":audio-radio-encrypted")); } } } break; case Qt::DisplayRole: switch (index.column()) { case 0: return channel.name; case 1: return channel.provider; case 2: return channel.snr; } break; case DvbPreviewChannelRole: return QVariant::fromValue(&channel); } return QVariant(); } QVariant DvbPreviewChannelTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation != Qt::Horizontal || role != Qt::DisplayRole) { return QVariant(); } switch (section) { case 0: return i18nc("@title:column tv show", "Channel"); case 1: return i18n("Provider"); case 2: return i18n("SNR"); } return QVariant(); } DvbScanDialog::DvbScanDialog(DvbManager *manager_, QWidget *parent) : QDialog(parent), manager(manager_), internal(NULL) { setWindowTitle(i18n("Channels")); QWidget *mainWidget = new QWidget(this); QBoxLayout *mainLayout = new QHBoxLayout(mainWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox, &QDialogButtonBox::accepted, this, &DvbScanDialog::dialogAccepted); connect(buttonBox, &QDialogButtonBox::rejected, this, &DvbScanDialog::reject); QGroupBox *groupBox = new QGroupBox(i18n("Channels"), mainWidget); QBoxLayout *groupLayout = new QVBoxLayout(groupBox); QBoxLayout *boxLayout = new QHBoxLayout(); channelModel = new DvbChannelModel(this); channelModel->cloneFrom(manager->getChannelModel()); DvbChannelTableModel *channelTableModel = new DvbChannelTableModel(this); DvbChannelView *channelView = new DvbChannelView(groupBox); channelView->setContextMenuPolicy(Qt::ActionsContextMenu); channelView->setDragDropMode(QAbstractItemView::InternalMove); channelView->setModel(channelTableModel); channelView->setRootIsDecorated(false); channelView->setSelectionMode(QAbstractItemView::ExtendedSelection); QHeaderView *header = manager->getChannelView()->header(); channelView->sortByColumn(header->sortIndicatorSection(), header->sortIndicatorOrder()); channelView->setSortingEnabled(true); channelTableModel->setChannelModel(channelModel); connect(channelTableModel, &DvbChannelTableModel::checkChannelDragAndDrop, channelView, &DvbChannelView::checkChannelDragAndDrop); QAction *action = channelView->addEditAction(); QPushButton *pushButton = new QPushButton(action->icon(), action->text(), groupBox); connect(pushButton, &QPushButton::clicked, channelView, &DvbChannelView::editChannel); boxLayout->addWidget(pushButton); action = channelView->addRemoveAction(); pushButton = new QPushButton(action->icon(), action->text(), groupBox); connect(pushButton, &QPushButton::clicked, channelView, &DvbChannelView::removeChannel); boxLayout->addWidget(pushButton); pushButton = new QPushButton(QIcon::fromTheme(QLatin1String("edit-clear-list"), QIcon(":edit-clear-list")), i18nc("remove all items from a list", "Clear"), groupBox); connect(pushButton, &QPushButton::clicked, channelView, &DvbChannelView::removeAllChannels); boxLayout->addWidget(pushButton); groupLayout->addLayout(boxLayout); groupLayout->addWidget(channelView); mainLayout->addWidget(groupBox); boxLayout = new QVBoxLayout(); groupBox = new QGroupBox(i18n("Channel Scan"), mainWidget); groupLayout = new QVBoxLayout(groupBox); groupLayout->addWidget(new QLabel(i18n("Source:"))); sourceBox = new QComboBox(groupBox); groupLayout->addWidget(sourceBox); otherNitCheckBox = new QCheckBox(i18n("Search transponders for other Networks"), groupBox); otherNitCheckBox->setWhatsThis(i18n("On certain networks, it is possible that some transponders are encoded on separate Network Information Tables (other NITs). This is more common on DVB-C systems. Clicking on this icon will change the scan algorithm to take those other NIT data into account. Please notice that the scan will be a lot more slow if enabled.")); groupLayout->addWidget(otherNitCheckBox); scanButton = new QPushButton(QIcon::fromTheme(QLatin1String("edit-find"), QIcon(":edit-find")), i18n("Start Scan"), groupBox); scanButton->setCheckable(true); connect(scanButton, &QPushButton::clicked, this, &DvbScanDialog::scanButtonClicked); groupLayout->addWidget(scanButton); QLabel *label = new QLabel(i18n("Scan data last updated on %1", QLocale().toString(manager->getScanDataDate(), QLocale::ShortFormat))); label->setWordWrap(true); groupLayout->addWidget(label); QGridLayout *gridLayout = new QGridLayout(); gridLayout->addWidget(new QLabel(i18n("Signal:")), 0, 0); signalWidget = new DvbGradProgress(groupBox); gridLayout->addWidget(signalWidget, 0, 1); gridLayout->addWidget(new QLabel(i18n("SNR:")), 1, 0); snrWidget = new DvbGradProgress(groupBox); gridLayout->addWidget(snrWidget, 1, 1); gridLayout->addWidget(new QLabel(i18n("Tuned:")), 2, 0); tunedLed = new KLed(groupBox); gridLayout->addWidget(tunedLed, 2, 1); groupLayout->addLayout(gridLayout); progressBar = new QProgressBar(groupBox); progressBar->setValue(0); groupLayout->addWidget(progressBar); boxLayout->addWidget(groupBox); boxLayout->addStretch(); groupBox = new QGroupBox(i18n("Filter"), mainWidget); groupLayout = new QVBoxLayout(groupBox); ftaCheckBox = new QCheckBox(i18n("Free to air"), groupBox); groupLayout->addWidget(ftaCheckBox); radioCheckBox = new QCheckBox(i18n("Radio"), groupBox); groupLayout->addWidget(radioCheckBox); tvCheckBox = new QCheckBox(i18n("TV"), groupBox); groupLayout->addWidget(tvCheckBox); providerCheckBox = new QCheckBox(i18n("Provider:"), groupBox); groupLayout->addWidget(providerCheckBox); providerBox = new QComboBox(groupBox); providerBox->setEnabled(false); connect(providerCheckBox, &QCheckBox::clicked, providerBox, &QComboBox::setEnabled); groupLayout->addWidget(providerBox); pushButton = new QPushButton(i18n("Add Filtered"), groupBox); connect(pushButton, &QPushButton::clicked, this, &DvbScanDialog::addFilteredChannels); groupLayout->addWidget(pushButton); pushButton = new QPushButton(i18n("Add Selected"), groupBox); connect(pushButton, &QPushButton::clicked, this, &DvbScanDialog::addSelectedChannels); groupLayout->addWidget(pushButton); boxLayout->addWidget(groupBox); mainLayout->addLayout(boxLayout); groupBox = new QGroupBox(i18n("Scan Results"), mainWidget); groupLayout = new QVBoxLayout(groupBox); previewModel = new DvbPreviewChannelTableModel(this); scanResultsView = new QTreeView(groupBox); scanResultsView->setModel(previewModel->createProxyModel(groupBox)); scanResultsView->setRootIsDecorated(false); scanResultsView->setSelectionMode(QAbstractItemView::ExtendedSelection); scanResultsView->sortByColumn(0, Qt::AscendingOrder); scanResultsView->setSortingEnabled(true); groupLayout->addWidget(scanResultsView); mainLayout->addWidget(groupBox); setDevice(manager->getLiveView()->getDevice()); if (device != NULL) { sourceBox->addItem(i18n("Current Transponder")); sourceBox->setEnabled(false); isLive = true; } else { QStringList list = manager->getSources(); if (!list.isEmpty()) { sourceBox->addItems(list); } else { sourceBox->setEnabled(false); scanButton->setEnabled(false); } isLive = false; } connect(&statusTimer, &QTimer::timeout, this, &DvbScanDialog::updateStatus); mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(mainWidget); mainLayout->addWidget(buttonBox); } DvbScanDialog::~DvbScanDialog() { if (!isLive && device) manager->releaseDevice(device, DvbManager::Exclusive); delete internal; } void DvbScanDialog::scanButtonClicked(bool checked) { if (!checked) { // stop scan Q_ASSERT(internal != NULL); scanButton->setText(i18n("Start Scan")); progressBar->setValue(0); delete internal; internal = NULL; if (!isLive) { manager->releaseDevice(device, DvbManager::Exclusive); setDevice(NULL); } return; } // start scan Q_ASSERT(internal == NULL); if (!manager->getLiveView()->getChannel().isValid()) { isLive = false; // FIXME workaround } if (isLive) { const DvbSharedChannel &channel = manager->getLiveView()->getChannel(); internal = new DvbScan(device, channel->source, channel->transponder, otherNitCheckBox->isChecked()); } else { QString source = sourceBox->currentText(); setDevice(manager->requestExclusiveDevice(source)); if (device != NULL) { // FIXME ugly QString autoScanSource = manager->getAutoScanSource(source); if (autoScanSource.isEmpty()) { internal = new DvbScan(device, source, manager->getTransponders(device, source), otherNitCheckBox->isChecked()); } else { internal = new DvbScan(device, source, autoScanSource, otherNitCheckBox->isChecked()); } } else { scanButton->setChecked(false); KMessageBox::sorry(this, i18nc("message box", "No available device found.")); return; } } scanButton->setText(i18n("Stop Scan")); providers.clear(); providerBox->clear(); previewModel->removeChannels(); connect(internal, &DvbScan::foundChannels, this, &DvbScanDialog::foundChannels); connect(internal, &DvbScan::scanProgress, progressBar, &QProgressBar::setValue); // calling scanFinished() will delete internal, so we have to queue the signal! connect(internal, &DvbScan::scanFinished, this, &DvbScanDialog::scanFinished, Qt::QueuedConnection); internal->start(); } void DvbScanDialog::dialogAccepted() { manager->getChannelModel()->cloneFrom(channelModel); manager->getChannelModel()->channelFlush(); QDialog::accept(); } static bool localeAwareLessThan2(const QString &x, const QString &y) { return x.localeAwareCompare(y) < 0; } void DvbScanDialog::foundChannels(const QList &channels) { previewModel->appendChannels(channels); foreach (const DvbPreviewChannel &channel, channels) { if (channel.provider.isEmpty()) { continue; } QStringList::const_iterator it = qLowerBound(providers.constBegin(), providers.constEnd(), channel.provider, localeAwareLessThan2); if ((it != providers.constEnd()) && (*it == channel.provider)) { continue; } int pos = it - providers.constBegin(); providers.insert(pos, channel.provider); providerBox->insertItem(pos, channel.provider); } } void DvbScanDialog::scanFinished() { // the state may have changed because the signal is queued if (scanButton->isChecked()) { scanButton->setChecked(false); scanButtonClicked(false); } } void DvbScanDialog::updateStatus() { if (device->getDeviceState() != DvbDevice::DeviceIdle) { DvbBackendDevice::Scale scaleSnr, scaleSignal; float signal = device->getSignal(scaleSignal); float snr = device->getSnr(scaleSnr); signalWidget->setValue(signal, scaleSignal); snrWidget->setValue(snr, scaleSnr); tunedLed->setState(device->isTuned() ? KLed::On : KLed::Off); } } void DvbScanDialog::addSelectedChannels() { QSet selectedRows; foreach (const QModelIndex &modelIndex, scanResultsView->selectionModel()->selectedIndexes()) { if (!selectedRows.contains(modelIndex.row())) { selectedRows.insert(modelIndex.row()); const DvbChannel *channel = scanResultsView->model()->data(modelIndex, DvbPreviewChannelTableModel::DvbPreviewChannelRole). value(); DvbChannel newChannel(*channel); channelModel->addChannel(newChannel); } } } void DvbScanDialog::addFilteredChannels() { foreach (const DvbPreviewChannel &channel, previewModel->getChannels()) { if (ftaCheckBox->isChecked()) { // only fta channels if (channel.isScrambled) { continue; } } if (radioCheckBox->isChecked()) { if (!tvCheckBox->isChecked()) { // only radio channels if (channel.hasVideo) { continue; } } } else { if (tvCheckBox->isChecked()) { // only tv channels if (!channel.hasVideo) { continue; } } } if (providerCheckBox->isChecked()) { // only channels from a certain provider if (channel.provider != providerBox->currentText()) { continue; } } DvbChannel newChannel(channel); channelModel->addChannel(newChannel); } } void DvbScanDialog::setDevice(DvbDevice *newDevice) { device = newDevice; if (device == NULL) { statusTimer.stop(); signalWidget->setValue(0, DvbBackendDevice::NotSupported); snrWidget->setValue(0, DvbBackendDevice::NotSupported); tunedLed->setState(KLed::Off); } else { statusTimer.start(1000); updateStatus(); } } diff --git a/src/dvb/dvbscandialog.h b/src/dvb/dvbscandialog.h index b284ac4..6946459 100644 --- a/src/dvb/dvbscandialog.h +++ b/src/dvb/dvbscandialog.h @@ -1,105 +1,105 @@ /* * dvbscandialog.h * * Copyright (C) 2008-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DVBSCANDIALOG_H #define DVBSCANDIALOG_H #include #include #include #include "dvbbackenddevice.h" class QCheckBox; class QProgressBar; class QTreeView; class QComboBox; class KLed; class DvbChannelModel; class DvbDevice; class DvbGradProgress; class DvbManager; class DvbPreviewChannel; class DvbPreviewChannelTableModel; class DvbScan; class DvbScanDialog : public QDialog { Q_OBJECT public: DvbScanDialog(DvbManager *manager_, QWidget *parent); ~DvbScanDialog(); private slots: void scanButtonClicked(bool checked); void dialogAccepted(); void foundChannels(const QList &channels); void scanFinished(); void updateStatus(); void addSelectedChannels(); void addFilteredChannels(); private: void setDevice(DvbDevice *newDevice); DvbManager *manager; DvbChannelModel *channelModel; QComboBox *sourceBox; QPushButton *scanButton; QProgressBar *progressBar; DvbGradProgress *signalWidget; DvbGradProgress *snrWidget; KLed *tunedLed; QCheckBox *otherNitCheckBox; QCheckBox *ftaCheckBox; QCheckBox *radioCheckBox; QCheckBox *tvCheckBox; QCheckBox *providerCheckBox; QStringList providers; QComboBox *providerBox; DvbPreviewChannelTableModel *previewModel; QTreeView *scanResultsView; DvbDevice *device; QTimer statusTimer; bool isLive; DvbScan *internal; }; class DvbGradProgress : public QLabel { public: explicit DvbGradProgress(QWidget *parent); ~DvbGradProgress(); void setValue(float value_, DvbBackendDevice::Scale scale); protected: - void paintEvent(QPaintEvent *event); + void paintEvent(QPaintEvent *event) override; private: float value, max, min; }; #endif /* DVBSCANDIALOG_H */ diff --git a/src/dvb/dvbsi.cpp b/src/dvb/dvbsi.cpp index d4c6903..b2efc0f 100644 --- a/src/dvb/dvbsi.cpp +++ b/src/dvb/dvbsi.cpp @@ -1,1765 +1,1765 @@ /* * dvbsi.cpp * * Copyright (C) 2008-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "../log.h" #include #include "dvbsi.h" void DvbSection::initSection(const char *data, int size) { if (size < 3) { initSectionData(); return; } int sectionLength = ((((quint8(data[1]) & 0xf) << 8) | quint8(data[2])) + 3); if (sectionLength > size) { qCInfo(logDvbSi, "Adjusting length"); sectionLength = size; } initSectionData(data, sectionLength, size); } int DvbStandardSection::verifyCrc32(const char *data, int size) { unsigned int crc32 = 0xffffffff; for (int i = 0; i < size; ++i) { crc32 = ((crc32 << 8) ^ crc32Table[(crc32 >> 24) ^ quint8(data[i])]); } return crc32; } /* * unsigned int crc32TableValue(int index) * { * unsigned int value = 0; * * for (int i = 8; i >= 0; --i) { * if (((value & 0x80000000) != 0) ^ ((index & (1 << i)) != 0)) { * value = (value << 1) ^ 0x04c11db7; * } else { * value <<= 1; * } * } * * return value; * } */ const unsigned int DvbStandardSection::crc32Table[] = { 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 }; void DvbStandardSection::initStandardSection(const char *data, int size) { if (size < 12) { initSectionData(); return; } initSection(data, size); } class Iso6937Codec : public QTextCodec { public: Iso6937Codec() { } ~Iso6937Codec() { } - QByteArray name() const + QByteArray name() const override { return "ISO 6937"; // actually a superset of ISO 6937 } - int mibEnum() const + int mibEnum() const override { return 14; } - QByteArray convertFromUnicode(const QChar *, int, QTextCodec::ConverterState *) const + QByteArray convertFromUnicode(const QChar *, int, QTextCodec::ConverterState *) const override { return QByteArray(); } - QString convertToUnicode(const char *input, int size, QTextCodec::ConverterState *) const + QString convertToUnicode(const char *input, int size, QTextCodec::ConverterState *) const override { QString result; unsigned short diacriticalMark = 0; for (; size > 0; ++input, --size) { unsigned short value = table[quint8(*input)]; if (value == 0xffff) { continue; } if ((value & 0xff00) == 0x0300) { // diacritical mark diacriticalMark = value; continue; } result.append(value); if (diacriticalMark != 0) { result.append(diacriticalMark); diacriticalMark = 0; } } return result.normalized(QString::NormalizationForm_C); } private: static const unsigned short table[]; }; const unsigned short Iso6937Codec::table[] = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x20ac, 0x00a5, 0xffff, 0x00a7, 0x00a4, 0x2018, 0x201c, 0x00ab, 0x2190, 0x2191, 0x2192, 0x2193, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00d7, 0x00b5, 0x00b6, 0x00b7, 0x00f7, 0x2019, 0x201d, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0xffff, 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0306, 0x0307, 0x0308, 0xffff, 0x030a, 0x0327, 0xffff, 0x030b, 0x0328, 0x030c, 0x2015, 0x00b9, 0x00ae, 0x00a9, 0x2122, 0x266a, 0x00ac, 0x00a6, 0xffff, 0xffff, 0xffff, 0xffff, 0x215b, 0x215c, 0x215d, 0x215e, 0x2126, 0x00c6, 0x0110, 0x00aa, 0x0126, 0xffff, 0x0132, 0x013f, 0x0141, 0x00d8, 0x0152, 0x00ba, 0x00de, 0x0166, 0x014a, 0x0149, 0x0138, 0x00e6, 0x0111, 0x00f0, 0x0127, 0x0131, 0x0133, 0x0140, 0x0142, 0x00f8, 0x0153, 0x00df, 0x00fe, 0x0167, 0x014b, 0x00ad }; QString DvbSiText::convertText(const char *data, int size) { TextEncoding encoding = Iso6937; if (size < 1) { return QString(); } if (size >2 && data[0] == 0x0e && data[size - 1] == 0x0f ) { // Remove LS0 and LS1 codes found on some ISDB-T streams data++; size -= 2; encoding = Iso8859_1; } if (override6937) encoding = Iso8859_1; if (quint8(data[0]) < 0x20) { switch (data[0]) { case 0x00: encoding = Iso6937; break; case 0x01: encoding = Iso8859_5; break; case 0x02: encoding = Iso8859_6; break; case 0x03: encoding = Iso8859_7; break; case 0x04: encoding = Iso8859_8; break; case 0x05: encoding = Iso8859_9; break; case 0x06: encoding = Iso8859_10; break; case 0x07: encoding = Iso8859_11; break; case 0x09: encoding = Iso8859_13; break; case 0x0a: encoding = Iso8859_14; break; case 0x0b: encoding = Iso8859_15; break; case 0x11: encoding = Iso10646_ucs2; break; case 0x12: encoding = Iso2022_kr; break; case 0x13: encoding = Gb2312; break; case 0x14: encoding = Utf_16be; break; case 0x15: encoding = Utf_8; break; case 0x10: { if (size < 3) { return QString(); } if (data[1] != 0) { return QString(); } switch (data[2]) { case 0x01: encoding = Iso8859_1; break; case 0x02: encoding = Iso8859_2; break; case 0x03: encoding = Iso8859_3; break; case 0x04: encoding = Iso8859_4; break; case 0x05: encoding = Iso8859_5; break; case 0x06: encoding = Iso8859_6; break; case 0x07: encoding = Iso8859_7; break; case 0x08: encoding = Iso8859_8; break; case 0x09: encoding = Iso8859_9; break; case 0x0a: encoding = Iso8859_10; break; case 0x0b: encoding = Iso8859_11; break; case 0x0d: encoding = Iso8859_13; break; case 0x0e: encoding = Iso8859_14; break; case 0x0f: encoding = Iso8859_15; break; default: return QString(); } data += 2; size -= 2; break; } } data++; size--; } if (codecTable[encoding] == NULL) { QTextCodec *codec = NULL; switch (encoding) { case Iso6937: codec = new Iso6937Codec(); break; case Iso8859_1: codec = QTextCodec::codecForName("ISO 8859-1"); break; case Iso8859_2: codec = QTextCodec::codecForName("ISO 8859-2"); break; case Iso8859_3: codec = QTextCodec::codecForName("ISO 8859-3"); break; case Iso8859_4: codec = QTextCodec::codecForName("ISO 8859-4"); break; case Iso8859_5: codec = QTextCodec::codecForName("ISO 8859-5"); break; case Iso8859_6: codec = QTextCodec::codecForName("ISO 8859-6"); break; case Iso8859_7: codec = QTextCodec::codecForName("ISO 8859-7"); break; case Iso8859_8: codec = QTextCodec::codecForName("ISO 8859-8"); break; case Iso8859_9: codec = QTextCodec::codecForName("ISO 8859-9"); break; case Iso8859_10: codec = QTextCodec::codecForName("ISO 8859-10"); break; case Iso8859_11: codec = QTextCodec::codecForName("ISO 8859-11"); break; case Iso8859_13: codec = QTextCodec::codecForName("ISO 8859-13"); break; case Iso8859_14: codec = QTextCodec::codecForName("ISO 8859-14"); break; case Iso8859_15: codec = QTextCodec::codecForName("ISO 8859-15"); break; case Iso10646_ucs2: codec = QTextCodec::codecForName("UTF-16"); break; case Iso2022_kr: codec = QTextCodec::codecForName("ISO 2022-KR"); break; case Gb2312: codec = QTextCodec::codecForName("GB2312"); break; case Utf_16be: codec = QTextCodec::codecForName("UTF-16BE"); break; case Utf_8: codec = QTextCodec::codecForName("UTF-8"); break; } Q_ASSERT(codec != NULL); codecTable[encoding] = codec; } if (encoding <= Iso8859_15) { // only strip control codes for one-byte character tables char *dest = new char[size]; char *destIt = dest; for (const char *it = data; it != (data + size); ++it) { unsigned char value = *it; if ((value < 0x80) || (value > 0x9f)) { *(destIt++) = value; } } QString result = codecTable[encoding]->toUnicode(dest, int(destIt - dest)); delete[] dest; return result; } return codecTable[encoding]->toUnicode(data, size); } void DvbSiText::setOverride6937(bool override) { override6937 = override; } QTextCodec *DvbSiText::codecTable[EncodingTypeMax + 1] = { NULL }; bool DvbSiText::override6937 = false; void DvbDescriptor::initDescriptor(const char *data, int size) { if (size < 2) { if (size != 0) { qCInfo(logDvbSi, "Invalid descriptor"); } initSectionData(); return; } int descriptorLength = (quint8(data[1]) + 2); if (descriptorLength > size) { qCInfo(logDvbSi, "Adjusting length"); descriptorLength = size; } initSectionData(data, descriptorLength, size); } QString AtscPsipText::interpretTextData(const char *data, unsigned int len, unsigned int mode) { // Based on the "mode" values in A/65C Table 6.41, convert // the data into a string QString result; if ((mode <= 0x06) || (mode >= 0x09 && mode <= 0x10) || (mode >= 0x20 && mode <= 0x27) || (mode >= 0x30 && mode <= 0x33)) { // Select UNICODE Code range based on mode as leading octet for (unsigned int i = 0; i < len; i++) { QChar val = (data[i] | (mode << 8)); result += val; } } else if (mode == 0x3f) { // UTF-16 for (unsigned int i = 0; i < len; i += 2) { QChar val = ((unsigned char)data[i] * 256) + (unsigned char)data[i+1]; result += val; } } else { // We currently don't support the following: // 0x3e Standard Compression Scheme for UNICODE (SCSU) // 0x40/0x41 Taiwan // 0x48 South Korea qCInfo(logDvbSi, "Unsupported ATSC Text mode %d", mode); } return result; } QString AtscPsipText::convertText(const char *data, int size) { QString result; if (size < 1) { return QString(); } unsigned int num_strings = quint8(data[0]); if (num_strings == 0) { return result; } // Note that for now we are only supporting the first string sent. If // multiple languages are sent, we only grab the first one provided int offset = 1; // First three bytes are the ISO 639 language code offset += 3; if (offset > size) { qCInfo(logDvbSi, "Adjusting length"); return result; } int num_segments = quint8(data[offset++]); for (int j = 0; j < num_segments; j++) { if ((offset + 3) > size) { qCInfo(logDvbSi, "Adjusting length"); return result; } int comp_type = quint8(data[offset++]); int mode = quint8(data[offset++]); int num_bytes = quint8(data[offset++]); if ((offset + num_bytes) > size) { qCInfo(logDvbSi, "Adjusting length"); return result; } const char *comp_string = (data + offset); if (comp_type == 0x00) { // Uncompressed result += interpretTextData(comp_string, num_bytes, mode); } else if ((comp_type == 0x01 || comp_type == 0x02) && mode == 0x00) { // Huffman Compression // As per the spec, only mode 0x00 supports Huffman coding result += AtscHuffmanString::convertText(comp_string, num_bytes, comp_type); } else { qCInfo(logDvbSi, "Unsupported compression / mode %d %d", comp_type, mode); } offset += num_bytes; } return result; } QString AtscHuffmanString::convertText(const char *data_, int length, int table) { AtscHuffmanString huffmanstring(data_, length, table); huffmanstring.decompress(); return huffmanstring.result; } AtscHuffmanString::AtscHuffmanString(const char *data_, int length, int table) : data(data_), bitsLeft(8 * length) { if (table == 1) { offsets = Huffman1Offsets; tableBase = Huffman1Tables; } else { offsets = Huffman2Offsets; tableBase = Huffman2Tables; } } AtscHuffmanString::~AtscHuffmanString() { } bool AtscHuffmanString::hasBits() { return bitsLeft > 0; } unsigned char AtscHuffmanString::getBit() { if (bitsLeft >= 1) { int shift = (--bitsLeft % 8); unsigned char value = (data[0] >> shift) & 0x1; if (shift == 0) { data++; } return value; } return 0; } unsigned char AtscHuffmanString::getByte() { if (bitsLeft >= 8) { int shift = ((bitsLeft - 1) % 8); // Note this takes advantage of the fact that there is always // at least one byte remaining (to avoid a read-past end // condition) unsigned char value = ((((data[0] << 8) | quint8(data[1])) >> (shift + 1)) & 0xff); bitsLeft -= 8; data++; return value; } return 0; } void AtscHuffmanString::decompress() { const unsigned char *table = tableBase; while (hasBits()) { int index = 0; do { index = table[2 * index + getBit()]; } while (index < 128); index &= 0x7f; if (index == 27) { // escape --> uncompressed character(s) while (true) { index = getByte(); if (index < 128) { break; } result += QChar(index); } } if (index == 0) { // end break; } result += QChar(index); table = tableBase + offsets[index]; } } const unsigned short AtscHuffmanString::Huffman1Offsets[128] = { 0x0000, 0x003a, 0x003c, 0x003e, 0x0040, 0x0042, 0x0044, 0x0046, 0x0048, 0x004a, 0x004c, 0x004e, 0x0050, 0x0052, 0x0054, 0x0056, 0x0058, 0x005a, 0x005c, 0x005e, 0x0060, 0x0062, 0x0064, 0x0066, 0x0068, 0x006a, 0x006c, 0x006e, 0x0070, 0x0072, 0x0074, 0x0076, 0x0078, 0x00ce, 0x00d2, 0x00d4, 0x00d6, 0x00d8, 0x00da, 0x00dc, 0x00e6, 0x00e8, 0x00ea, 0x00f0, 0x00f2, 0x00f4, 0x0106, 0x0112, 0x0114, 0x011c, 0x0128, 0x0130, 0x0134, 0x0136, 0x0138, 0x013a, 0x013c, 0x013e, 0x0146, 0x0148, 0x014a, 0x014c, 0x014e, 0x0150, 0x0152, 0x0154, 0x017e, 0x0192, 0x01ac, 0x01ba, 0x01d2, 0x01e4, 0x01fa, 0x0206, 0x021e, 0x0226, 0x0232, 0x023e, 0x0252, 0x0264, 0x027a, 0x0294, 0x0298, 0x02a4, 0x02c8, 0x02de, 0x02e6, 0x02f4, 0x0304, 0x0306, 0x030c, 0x0310, 0x0312, 0x0314, 0x0316, 0x0318, 0x031a, 0x031c, 0x0352, 0x036a, 0x038e, 0x03ae, 0x03ee, 0x0406, 0x0428, 0x0444, 0x0472, 0x0476, 0x0490, 0x04be, 0x04d6, 0x050a, 0x0544, 0x0564, 0x0566, 0x059a, 0x05d0, 0x05fc, 0x0622, 0x062c, 0x0646, 0x0654, 0x067c, 0x068a, 0x068c, 0x068e, 0x0690, 0x0692 }; const unsigned char AtscHuffmanString::Huffman1Tables[] = { 0x1b, 0x1c, 0xb4, 0xa4, 0xb2, 0xb7, 0xda, 0x01, 0xd1, 0x02, 0x03, 0x9b, 0x04, 0xd5, 0xd9, 0x05, 0xcb, 0xd6, 0x06, 0xcf, 0x07, 0x08, 0xca, 0x09, 0xc9, 0xc5, 0xc6, 0x0a, 0xd2, 0xc4, 0xc7, 0xcc, 0xd0, 0xc8, 0xd7, 0xce, 0x0b, 0xc1, 0x0c, 0xc2, 0xcd, 0xc3, 0x0d, 0x0e, 0x0f, 0x10, 0xd3, 0x11, 0xd4, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x29, 0x2a, 0xd8, 0xe5, 0xb9, 0x01, 0xa7, 0xb1, 0xec, 0xd1, 0x02, 0xad, 0xb2, 0xda, 0xe3, 0xb3, 0x03, 0xe4, 0xe6, 0x04, 0x9b, 0xe2, 0x05, 0x06, 0x07, 0x08, 0x09, 0xd5, 0x0a, 0xd6, 0x0b, 0xd9, 0x0c, 0xa6, 0xe9, 0xcb, 0xc5, 0xcf, 0x0d, 0x0e, 0xca, 0xc9, 0x0f, 0xc7, 0x10, 0x11, 0xe1, 0x12, 0x13, 0xc6, 0xd2, 0xc8, 0xce, 0xc1, 0xc4, 0xd0, 0xcc, 0x14, 0x15, 0xef, 0xc2, 0xd7, 0x16, 0xcd, 0x17, 0xf4, 0xd4, 0x18, 0x19, 0x1a, 0xc3, 0xd3, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x01, 0x80, 0xa0, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0xb1, 0x9b, 0x9b, 0x9b, 0x9b, 0xa0, 0x04, 0xf3, 0xe4, 0xb9, 0x01, 0xf4, 0xa0, 0x9b, 0x02, 0x03, 0x9b, 0x9b, 0x9b, 0x9b, 0x01, 0x02, 0x9b, 0xc1, 0xc8, 0xd3, 0x9b, 0x9b, 0x9b, 0xa0, 0x07, 0x08, 0xb1, 0xd2, 0xd3, 0xd4, 0xd5, 0xad, 0xcd, 0xc1, 0x01, 0x02, 0x03, 0xa0, 0x04, 0x9b, 0x05, 0x06, 0xa0, 0x05, 0xc9, 0xd7, 0xd3, 0x01, 0x02, 0x9b, 0xae, 0x80, 0x03, 0x04, 0x9b, 0x9b, 0x02, 0x03, 0xad, 0x9b, 0x01, 0x80, 0xa0, 0xb0, 0x04, 0x05, 0x80, 0x9b, 0xb1, 0xb2, 0xa0, 0xb0, 0xb9, 0x01, 0x02, 0x03, 0x02, 0x03, 0xb1, 0xba, 0x01, 0xb0, 0x9b, 0x80, 0x80, 0x01, 0xb0, 0x9b, 0x9b, 0xb8, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0xb0, 0x9b, 0xa0, 0x02, 0x03, 0xb1, 0xb3, 0xb9, 0xb0, 0x01, 0x9b, 0x9b, 0xa0, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x80, 0x9b, 0x9b, 0x13, 0x14, 0xaa, 0xad, 0xae, 0xf6, 0xe7, 0xf4, 0xe2, 0xe9, 0x01, 0x02, 0xc2, 0xf0, 0x9b, 0xf3, 0xe3, 0xe6, 0xf7, 0x03, 0xf5, 0x04, 0x05, 0x06, 0xf2, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0xe4, 0xa0, 0x0d, 0xec, 0xee, 0x0e, 0xed, 0x0f, 0x10, 0x11, 0x12, 0x08, 0x09, 0xc1, 0xd3, 0x9b, 0x01, 0xc3, 0x02, 0xe9, 0xec, 0x03, 0xf2, 0xf5, 0x04, 0xef, 0xe1, 0x05, 0xe5, 0x06, 0x07, 0x0b, 0x0c, 0xc1, 0xf9, 0x01, 0xc2, 0xcf, 0xe5, 0xf5, 0x9b, 0xe9, 0x02, 0xa0, 0x03, 0x04, 0x05, 0xf2, 0x06, 0xec, 0x07, 0xe1, 0x08, 0x09, 0xe8, 0x0a, 0xef, 0x05, 0x06, 0xf9, 0x9b, 0x01, 0xf5, 0x02, 0xf2, 0xe9, 0xe5, 0xef, 0x03, 0xe1, 0x04, 0x0a, 0x0b, 0xf1, 0xf5, 0xf3, 0x01, 0xed, 0xf9, 0xc3, 0x02, 0xec, 0xee, 0xe4, 0xf8, 0x03, 0x9b, 0xf6, 0x04, 0x05, 0xe1, 0x06, 0x07, 0x08, 0x09, 0x07, 0x08, 0xa0, 0x9b, 0xcc, 0x01, 0xe5, 0x02, 0xec, 0xf5, 0xef, 0x03, 0xe9, 0xf2, 0x04, 0x05, 0xe1, 0x06, 0x09, 0x0a, 0xae, 0xec, 0xf9, 0xc1, 0xe8, 0x01, 0x9b, 0x02, 0x03, 0x04, 0xe1, 0xf5, 0xe9, 0x05, 0xe5, 0x06, 0xf2, 0xef, 0x07, 0x08, 0xef, 0x05, 0x80, 0x9b, 0xf5, 0x01, 0x02, 0xe9, 0xe1, 0x03, 0xe5, 0x04, 0xee, 0x0b, 0xba, 0xd4, 0xae, 0xf2, 0xe3, 0x01, 0xa0, 0x02, 0x80, 0x9b, 0xed, 0x03, 0xc9, 0xf3, 0xf4, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x02, 0x03, 0x9b, 0xf5, 0x01, 0xe1, 0xef, 0xe5, 0x05, 0xe9, 0xe1, 0xef, 0xf5, 0xee, 0x9b, 0xe5, 0x01, 0x02, 0x03, 0x04, 0x04, 0x05, 0xa0, 0x9b, 0x01, 0xf5, 0x02, 0xe5, 0xef, 0x03, 0xe1, 0xe9, 0x08, 0x09, 0xaa, 0xd4, 0x01, 0x9b, 0xe3, 0x02, 0xf2, 0x03, 0xe5, 0x04, 0xf5, 0xf9, 0xe9, 0x05, 0xef, 0x06, 0x07, 0xe1, 0xe5, 0x08, 0xce, 0xa0, 0xc6, 0xf5, 0x01, 0x02, 0x9b, 0xc2, 0x03, 0xe1, 0x04, 0xef, 0x05, 0xe9, 0x06, 0x07, 0x09, 0x0a, 0xe4, 0xf3, 0xe6, 0xf6, 0xf7, 0xf0, 0xf2, 0x01, 0xec, 0x02, 0x03, 0xa0, 0x9b, 0x04, 0x05, 0xf5, 0x06, 0x07, 0xee, 0x08, 0x0b, 0x0c, 0xa0, 0xf3, 0xf9, 0xae, 0xd2, 0xc7, 0x01, 0x9b, 0x02, 0xf5, 0x03, 0x04, 0x05, 0xe9, 0xec, 0x06, 0xe5, 0x07, 0xef, 0x08, 0xe1, 0x09, 0xf2, 0x0a, 0x01, 0xf5, 0x9b, 0xd6, 0x04, 0x05, 0xe8, 0x9b, 0x01, 0xf5, 0x02, 0xe1, 0xe9, 0xef, 0x03, 0xe5, 0x10, 0x11, 0xaa, 0xec, 0xf1, 0xae, 0xa0, 0xf7, 0xed, 0xee, 0x01, 0x02, 0x9b, 0xeb, 0x03, 0x04, 0x05, 0x06, 0xe3, 0x07, 0xef, 0x08, 0xe9, 0xf5, 0x09, 0xe1, 0xe5, 0xf0, 0xe8, 0x0a, 0x0b, 0x0c, 0x0d, 0xf4, 0x0e, 0x0f, 0xe8, 0x0a, 0xad, 0xce, 0x9b, 0x01, 0xd6, 0x02, 0xf5, 0xf7, 0x03, 0x04, 0xe1, 0xe5, 0xe9, 0x05, 0xf2, 0x06, 0xef, 0x07, 0x08, 0x09, 0xee, 0x03, 0xec, 0xae, 0x01, 0x9b, 0x02, 0xf0, 0x06, 0xe9, 0xa0, 0xc3, 0xef, 0x9b, 0xe5, 0x01, 0x80, 0x02, 0x03, 0xe1, 0x04, 0x05, 0x06, 0x07, 0xc6, 0xd7, 0x01, 0x9b, 0xf2, 0x02, 0x03, 0xe8, 0xe5, 0xe1, 0x04, 0xe9, 0xef, 0x05, 0x9b, 0x9b, 0x02, 0xef, 0xe1, 0x9b, 0x01, 0xe5, 0x01, 0xef, 0x9b, 0xe1, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x19, 0x1a, 0x9b, 0xba, 0xe5, 0xea, 0xf8, 0x01, 0x02, 0xe6, 0xa7, 0x03, 0xfa, 0xe8, 0x04, 0xf7, 0x05, 0xf5, 0xe2, 0x06, 0xeb, 0x07, 0xf0, 0x08, 0x80, 0xf6, 0xe7, 0x09, 0xe4, 0x0a, 0xa0, 0xe9, 0x0b, 0xe3, 0xf9, 0x0c, 0x0d, 0xed, 0x0e, 0x0f, 0xf3, 0x10, 0x11, 0xec, 0x12, 0xf4, 0xf2, 0x13, 0xee, 0x14, 0x15, 0x16, 0x17, 0x18, 0x0a, 0x0b, 0xf3, 0x9b, 0xf5, 0xe2, 0x01, 0x80, 0xa0, 0x02, 0xe5, 0xf2, 0xe9, 0x03, 0xec, 0x04, 0xf9, 0x05, 0xef, 0x06, 0xe1, 0x07, 0x08, 0x09, 0x10, 0x11, 0xc3, 0xcc, 0xc7, 0x9b, 0xe3, 0x01, 0x80, 0xec, 0xf9, 0x02, 0xf3, 0x03, 0xf5, 0x04, 0x05, 0xf2, 0x06, 0xe9, 0xa0, 0x07, 0x08, 0xef, 0xf4, 0x09, 0x0a, 0xe1, 0x0b, 0xe8, 0xeb, 0xe5, 0x0c, 0x0d, 0x0e, 0x0f, 0x0e, 0x0f, 0xae, 0xf5, 0xf7, 0x01, 0xec, 0x02, 0xe4, 0xe7, 0xf2, 0x03, 0x9b, 0xef, 0x04, 0xf6, 0x05, 0x06, 0xf9, 0xf3, 0x07, 0xe9, 0xe1, 0x08, 0x09, 0x80, 0x0a, 0x0b, 0xe5, 0x0c, 0x0d, 0xa0, 0x1e, 0x1f, 0x9b, 0xa1, 0xad, 0xe8, 0xea, 0xf1, 0xf5, 0xfa, 0x01, 0x02, 0x03, 0x04, 0xba, 0xf8, 0xa7, 0xe2, 0xe9, 0x05, 0x06, 0x07, 0xe6, 0xed, 0xe7, 0xeb, 0x08, 0x09, 0xf6, 0xf0, 0x0a, 0xef, 0x0b, 0xe3, 0x0c, 0x0d, 0x0e, 0xf9, 0x0f, 0xe4, 0xec, 0x10, 0xe5, 0x11, 0xf4, 0xf7, 0x12, 0x13, 0xe1, 0x14, 0x15, 0x16, 0xee, 0xf3, 0x17, 0x80, 0x18, 0x19, 0xf2, 0x1a, 0x1b, 0xa0, 0x1c, 0x1d, 0xa0, 0x0b, 0xf5, 0x9b, 0x01, 0xec, 0xf3, 0xf2, 0x80, 0xe1, 0x02, 0x03, 0xf4, 0xe9, 0xef, 0xe6, 0x04, 0x05, 0x06, 0x07, 0xe5, 0x08, 0x09, 0x0a, 0x0f, 0x10, 0xba, 0xf9, 0xa7, 0xf4, 0x9b, 0x01, 0xe7, 0xec, 0x02, 0xee, 0x03, 0xef, 0xf5, 0x04, 0xf2, 0x05, 0x06, 0xe9, 0x07, 0xf3, 0xe1, 0x08, 0x09, 0x0a, 0x0b, 0xe5, 0x80, 0x0c, 0xe8, 0xa0, 0x0d, 0x0e, 0xe5, 0x0d, 0xe2, 0xf5, 0xf7, 0x9b, 0xec, 0x01, 0xf9, 0xee, 0x02, 0x03, 0x04, 0xf2, 0x05, 0x80, 0x06, 0xa0, 0xe1, 0xef, 0x07, 0xf4, 0xe9, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x15, 0x16, 0xa1, 0xf8, 0xe9, 0xeb, 0x01, 0x80, 0x9b, 0xfa, 0xe2, 0x02, 0x03, 0x04, 0xa0, 0xf0, 0x05, 0x06, 0x07, 0xe1, 0x08, 0xe6, 0xf2, 0xed, 0xf6, 0x09, 0xe4, 0x0a, 0xef, 0xf4, 0xec, 0xf3, 0xe7, 0xe5, 0x0b, 0xe3, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0xee, 0x14, 0xef, 0x01, 0x9b, 0xe1, 0x0b, 0x0c, 0xd4, 0xef, 0xe6, 0xec, 0xf7, 0xe1, 0x01, 0xba, 0x02, 0x9b, 0xf9, 0x03, 0x04, 0x05, 0xf3, 0x06, 0x07, 0x08, 0xe9, 0xa0, 0x09, 0x80, 0xe5, 0x0a, 0x15, 0x16, 0xa7, 0xba, 0xe3, 0xf7, 0xf2, 0xad, 0xe2, 0x01, 0x02, 0x9b, 0xe6, 0x03, 0xed, 0xf6, 0x04, 0xeb, 0x05, 0xf4, 0x06, 0x07, 0x08, 0xf3, 0x09, 0xf5, 0x0a, 0xef, 0x0b, 0x0c, 0x80, 0xf9, 0xe1, 0x0d, 0xe4, 0xe9, 0xa0, 0x0e, 0x0f, 0xec, 0xe5, 0x10, 0x11, 0x12, 0x13, 0x14, 0x0a, 0x0b, 0xf9, 0x9b, 0xf5, 0xf3, 0x01, 0x02, 0xe2, 0xed, 0x80, 0x03, 0xf0, 0xef, 0x04, 0xa0, 0x05, 0xe9, 0x06, 0xe1, 0x07, 0x08, 0x09, 0xe5, 0x18, 0x19, 0xe2, 0xea, 0xf2, 0xe8, 0xec, 0xed, 0xfa, 0x9b, 0x01, 0xf5, 0x02, 0x03, 0xf6, 0x04, 0xba, 0xe6, 0x05, 0x06, 0xeb, 0xef, 0x07, 0xa7, 0xf9, 0x08, 0x09, 0x0a, 0x0b, 0xe3, 0x0c, 0xee, 0xe1, 0x0d, 0xf3, 0x0e, 0xe9, 0x0f, 0x10, 0xf4, 0x80, 0xe4, 0xe5, 0x11, 0x12, 0xe7, 0xa0, 0x13, 0x14, 0x15, 0x16, 0x17, 0x1b, 0x1c, 0xae, 0xfa, 0xbf, 0x01, 0xa7, 0x9b, 0x02, 0xe9, 0xf8, 0xf9, 0x03, 0xe5, 0xe8, 0x04, 0xe1, 0xeb, 0x05, 0xe2, 0x06, 0x07, 0xe3, 0x08, 0xe7, 0xf4, 0x09, 0x80, 0xf6, 0xf0, 0x0a, 0xe4, 0x0b, 0xf3, 0xf7, 0x0c, 0x0d, 0xef, 0xec, 0xa0, 0x0e, 0x0f, 0xed, 0xe6, 0x10, 0xf5, 0x11, 0x12, 0x13, 0x14, 0x15, 0xf2, 0x16, 0xee, 0x17, 0x18, 0x19, 0x1a, 0x0e, 0x0f, 0xed, 0xa7, 0x9b, 0xe4, 0x01, 0xf9, 0xf3, 0xf2, 0xf4, 0x02, 0xe8, 0x03, 0xec, 0xf0, 0x04, 0xe1, 0xe9, 0x05, 0x06, 0x80, 0xa0, 0x07, 0x08, 0x09, 0x0a, 0xe5, 0xef, 0x0b, 0x0c, 0x0d, 0x9b, 0xf5, 0x18, 0x19, 0xba, 0xac, 0xf6, 0x9b, 0xf0, 0xe2, 0x01, 0xe6, 0x02, 0xa7, 0xae, 0xe7, 0x03, 0xe3, 0xf5, 0x04, 0xed, 0x05, 0x06, 0x07, 0xeb, 0x08, 0x09, 0xee, 0xf2, 0x0a, 0xe4, 0x0b, 0xf9, 0xec, 0x0c, 0x0d, 0xf4, 0x80, 0x0e, 0xef, 0xf3, 0xa0, 0xe1, 0x0f, 0xe9, 0x10, 0x11, 0xe5, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x19, 0x1a, 0xa7, 0xac, 0xbf, 0xc3, 0xc8, 0xe4, 0xe6, 0xed, 0xf2, 0xae, 0xec, 0xee, 0xf9, 0x01, 0x02, 0x03, 0x04, 0xba, 0x05, 0x9b, 0xf5, 0x06, 0x07, 0x08, 0x09, 0xeb, 0xf0, 0x0a, 0x0b, 0x0c, 0xe1, 0xe3, 0x0d, 0xe8, 0x0e, 0x0f, 0xef, 0x10, 0x11, 0xf3, 0x12, 0xe9, 0x13, 0xe5, 0x14, 0x15, 0xf4, 0x16, 0x17, 0xa0, 0x18, 0x80, 0x14, 0x15, 0xba, 0xbf, 0xe4, 0xf7, 0x9b, 0xa7, 0x01, 0xee, 0x02, 0x03, 0x04, 0xe3, 0xe2, 0xed, 0x05, 0xf9, 0x06, 0xf4, 0x07, 0xec, 0x08, 0xf5, 0xf2, 0x09, 0xe1, 0xf3, 0x0a, 0xef, 0x0b, 0x0c, 0x0d, 0xe9, 0x80, 0xe5, 0x0e, 0xa0, 0x0f, 0xe8, 0x10, 0x11, 0x12, 0x13, 0x11, 0x12, 0xeb, 0xfa, 0x80, 0xe6, 0x9b, 0x01, 0xa0, 0x02, 0x03, 0xe9, 0xe1, 0x04, 0xe4, 0xf0, 0xed, 0xe2, 0xe3, 0xe7, 0xec, 0x05, 0xe5, 0x06, 0x07, 0x08, 0x09, 0xf4, 0x0a, 0x0b, 0x0c, 0xf3, 0xee, 0x0d, 0x0e, 0xf2, 0x0f, 0x10, 0x04, 0xe5, 0xf3, 0xef, 0x9b, 0x01, 0xe1, 0x02, 0x03, 0xe9, 0x0b, 0x0c, 0xa7, 0xe2, 0xec, 0xe3, 0xf2, 0x01, 0x9b, 0x02, 0x03, 0x04, 0xe9, 0xef, 0xee, 0xe5, 0xe1, 0x80, 0x05, 0xa0, 0x06, 0x07, 0x08, 0x09, 0xf3, 0x0a, 0x05, 0x06, 0x9b, 0xa0, 0xe1, 0xe5, 0xe9, 0x01, 0x80, 0xf0, 0x02, 0xf4, 0x03, 0x04, 0xa0, 0x13, 0xe3, 0xad, 0xe4, 0xe9, 0xee, 0xef, 0xf0, 0xf4, 0xf6, 0xa1, 0xe1, 0xed, 0x01, 0xe2, 0x02, 0x03, 0x04, 0xa7, 0x05, 0x06, 0xf7, 0x07, 0x9b, 0xec, 0x08, 0xe5, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf3, 0x0f, 0x10, 0x11, 0x80, 0x12, 0x05, 0x06, 0xe5, 0xfa, 0xa0, 0xf9, 0x9b, 0x01, 0x80, 0xe9, 0x02, 0xe1, 0x03, 0x04, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b }; const unsigned short AtscHuffmanString::Huffman2Offsets[128] = { 0x0000, 0x002c, 0x002e, 0x0030, 0x0032, 0x0034, 0x0036, 0x0038, 0x003a, 0x003c, 0x003e, 0x0040, 0x0042, 0x0044, 0x0046, 0x0048, 0x004a, 0x004c, 0x004e, 0x0050, 0x0052, 0x0054, 0x0056, 0x0058, 0x005a, 0x005c, 0x005e, 0x0060, 0x0062, 0x0064, 0x0066, 0x0068, 0x006a, 0x00de, 0x00e0, 0x00ea, 0x00ec, 0x00ee, 0x00f0, 0x00f2, 0x00f8, 0x00fa, 0x00fc, 0x00fe, 0x0100, 0x0104, 0x0116, 0x0120, 0x0122, 0x012c, 0x0132, 0x0138, 0x013c, 0x0140, 0x0144, 0x0146, 0x014a, 0x014c, 0x0154, 0x0156, 0x0158, 0x015a, 0x015c, 0x015e, 0x0160, 0x0162, 0x0176, 0x0184, 0x0194, 0x01a2, 0x01b2, 0x01ba, 0x01c8, 0x01d2, 0x01de, 0x01ea, 0x01f2, 0x01fc, 0x0208, 0x0210, 0x021a, 0x0228, 0x022a, 0x0234, 0x024a, 0x025a, 0x025e, 0x0264, 0x026e, 0x0270, 0x0272, 0x0274, 0x0276, 0x0278, 0x027a, 0x027c, 0x027e, 0x0280, 0x02b4, 0x02ce, 0x02f0, 0x031a, 0x0358, 0x036e, 0x038e, 0x03ac, 0x03d8, 0x03e0, 0x03f4, 0x0424, 0x0440, 0x0476, 0x04ae, 0x04ce, 0x04d0, 0x0506, 0x0534, 0x0560, 0x0586, 0x0592, 0x05aa, 0x05b8, 0x05dc, 0x05ec, 0x05ee, 0x05f0, 0x05f2, 0x05f4 }; const unsigned char AtscHuffmanString::Huffman2Tables[] = { 0x14, 0x15, 0x9b, 0xd6, 0xc9, 0xcf, 0xd7, 0xc7, 0x01, 0xa2, 0xce, 0xcb, 0x02, 0x03, 0xc5, 0xcc, 0xc6, 0xc8, 0x04, 0xc4, 0x05, 0xc2, 0x06, 0xc3, 0xd2, 0x07, 0xd3, 0x08, 0xca, 0xd4, 0x09, 0xcd, 0xd0, 0x0a, 0xc1, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x38, 0x39, 0xad, 0xaf, 0xb7, 0xda, 0xa8, 0xb3, 0xb5, 0x01, 0x02, 0x9b, 0xb4, 0xf1, 0xa2, 0xd5, 0xd6, 0xd9, 0x03, 0x04, 0x05, 0xcf, 0x06, 0xc9, 0xf9, 0xea, 0xeb, 0xf5, 0xf6, 0x07, 0x08, 0x09, 0xb2, 0xc5, 0xc6, 0xb1, 0x0a, 0xee, 0xcb, 0x0b, 0xd4, 0x0c, 0xc4, 0xc8, 0xd2, 0x0d, 0x0e, 0x0f, 0xc7, 0xca, 0xce, 0xd0, 0xd7, 0x10, 0xc2, 0x11, 0xcc, 0xec, 0xe5, 0xe7, 0x12, 0xcd, 0x13, 0x14, 0xc3, 0x15, 0x16, 0x17, 0xed, 0x18, 0x19, 0xf2, 0x1a, 0xd3, 0x1b, 0x1c, 0xe4, 0x1d, 0xc1, 0xe3, 0x1e, 0xe9, 0xf0, 0xe2, 0xf7, 0x1f, 0xf3, 0xe6, 0x20, 0x21, 0x22, 0xe8, 0xef, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0xf4, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0xe1, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x9b, 0x9b, 0x03, 0x04, 0x80, 0xae, 0xc8, 0xd4, 0x01, 0x02, 0x9b, 0xa0, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x02, 0xf3, 0xa0, 0xf4, 0x9b, 0x01, 0x9b, 0x9b, 0xac, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x01, 0xa0, 0x9b, 0xa2, 0x07, 0x08, 0xe2, 0xe4, 0xe5, 0xe6, 0xa0, 0xf2, 0xe1, 0x01, 0x02, 0xf3, 0xe3, 0x03, 0x04, 0x05, 0x9b, 0x06, 0x04, 0x80, 0xca, 0xd3, 0xa2, 0x01, 0x9b, 0x02, 0x03, 0xa0, 0x9b, 0xa0, 0x03, 0x04, 0x9b, 0xb7, 0xf4, 0xa0, 0xb0, 0xf3, 0x01, 0x02, 0xb9, 0x02, 0xb8, 0x9b, 0xa0, 0x01, 0xae, 0x02, 0xb6, 0x9b, 0x01, 0xa0, 0xa0, 0x01, 0x9b, 0xb0, 0xae, 0x01, 0x9b, 0xa0, 0xae, 0x01, 0xa0, 0x9b, 0x9b, 0x9b, 0x9b, 0x01, 0xac, 0xae, 0x9b, 0x9b, 0x02, 0x03, 0x9b, 0xa0, 0xb5, 0xb6, 0xb8, 0x01, 0x9b, 0xa0, 0x9b, 0xa0, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0xa0, 0x9b, 0x9b, 0x08, 0x09, 0xe6, 0xf5, 0xf3, 0xf4, 0x9b, 0xe4, 0x01, 0xed, 0x02, 0x03, 0x04, 0xf2, 0x05, 0x06, 0xec, 0xee, 0x07, 0xa0, 0x05, 0x06, 0x9b, 0xec, 0xf5, 0x01, 0x02, 0xe1, 0xef, 0xe5, 0xe9, 0xf2, 0x03, 0x04, 0x06, 0x07, 0x9b, 0xe9, 0xf9, 0xf2, 0xf5, 0x01, 0x02, 0x03, 0xec, 0xef, 0xe1, 0x04, 0xe8, 0x05, 0x05, 0x06, 0xf9, 0xf2, 0xf5, 0x9b, 0xe5, 0xef, 0x01, 0x02, 0xe9, 0xe1, 0x03, 0x04, 0x06, 0x07, 0xe1, 0xe9, 0xee, 0xf6, 0xe4, 0xec, 0xf3, 0x01, 0x02, 0xf2, 0x03, 0x04, 0x9b, 0x05, 0x02, 0x03, 0xe5, 0xec, 0x9b, 0xef, 0x01, 0xf2, 0x05, 0x06, 0xf5, 0xef, 0x9b, 0xec, 0xe9, 0x01, 0xe1, 0xf2, 0x02, 0xe5, 0x03, 0x04, 0x03, 0x04, 0x9b, 0xe5, 0xe9, 0xf5, 0xe1, 0x01, 0xef, 0x02, 0x04, 0x05, 0xa0, 0xc9, 0xf3, 0x9b, 0xae, 0xf2, 0x01, 0x02, 0x03, 0xee, 0xef, 0x05, 0x9b, 0xae, 0xe9, 0xe5, 0x01, 0xf5, 0x02, 0xe1, 0x03, 0x04, 0xe5, 0x03, 0xe1, 0xe9, 0xf2, 0x9b, 0x01, 0x02, 0x03, 0x04, 0x9b, 0xe9, 0xf5, 0x01, 0xe5, 0x02, 0xef, 0xe1, 0xe1, 0x05, 0x9b, 0xe3, 0xef, 0x01, 0xf5, 0xe5, 0x02, 0x03, 0xe9, 0x04, 0xe5, 0x03, 0x9b, 0xe9, 0x01, 0xe1, 0xef, 0x02, 0x03, 0x04, 0xa7, 0xee, 0xec, 0xf2, 0xf3, 0x01, 0x9b, 0x02, 0xe1, 0x06, 0x9b, 0xe8, 0xe9, 0x01, 0xf2, 0xec, 0x02, 0xef, 0x03, 0xe5, 0x04, 0x05, 0x9b, 0x9b, 0x03, 0x04, 0x9b, 0xae, 0x01, 0xe9, 0x02, 0xe1, 0xe5, 0xef, 0x09, 0x0a, 0xf6, 0xf9, 0x01, 0xae, 0xe3, 0xe9, 0xf5, 0x9b, 0xe5, 0xef, 0x02, 0x03, 0xe1, 0x04, 0xe8, 0x05, 0x06, 0xf4, 0x07, 0x08, 0xe8, 0x07, 0xe5, 0xf7, 0xd6, 0xe1, 0x9b, 0xe9, 0xf2, 0x01, 0x02, 0x03, 0x04, 0xef, 0x05, 0x06, 0xae, 0x01, 0x9b, 0xee, 0xe9, 0x02, 0xe5, 0x9b, 0xa0, 0x01, 0x03, 0x04, 0x9b, 0xe8, 0xe5, 0xe1, 0xef, 0x01, 0xe9, 0x02, 0x9b, 0x9b, 0x9b, 0xef, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x18, 0x19, 0xe8, 0xef, 0xf8, 0x9b, 0xa7, 0xf7, 0xfa, 0x01, 0x02, 0x03, 0x04, 0xe5, 0xae, 0x05, 0xe6, 0xe2, 0x06, 0xf6, 0xeb, 0xf5, 0xe9, 0x07, 0xf0, 0xf9, 0xe7, 0x08, 0x09, 0xe4, 0x0a, 0xe3, 0x0b, 0xed, 0x0c, 0xf3, 0x0d, 0x0e, 0x0f, 0xec, 0x10, 0xf4, 0x11, 0x12, 0xf2, 0xa0, 0x13, 0x14, 0x15, 0xee, 0x16, 0x17, 0x0b, 0x0c, 0xe4, 0xf3, 0x9b, 0xae, 0xe2, 0x01, 0x02, 0x03, 0xec, 0xa0, 0x04, 0xe9, 0xf2, 0xf5, 0x05, 0xf9, 0xe1, 0x06, 0xef, 0x07, 0xe5, 0x08, 0x09, 0x0a, 0x0f, 0x10, 0xf1, 0xae, 0xc4, 0xf9, 0xac, 0x01, 0xe3, 0x02, 0x9b, 0xf2, 0x03, 0x04, 0xa0, 0xec, 0xf5, 0x05, 0x06, 0xe9, 0x07, 0xeb, 0x08, 0xf4, 0x09, 0xe5, 0x0a, 0xef, 0xe1, 0xe8, 0x0b, 0x0c, 0x0d, 0x0e, 0x13, 0x14, 0xa7, 0xbb, 0xe6, 0xed, 0xf7, 0xe7, 0xf6, 0x01, 0x02, 0x9b, 0xee, 0x03, 0x04, 0xec, 0x05, 0xf5, 0x06, 0xac, 0xe4, 0xf9, 0xf2, 0x07, 0x08, 0x09, 0xae, 0x0a, 0xef, 0x0b, 0xe1, 0xf3, 0x0c, 0xe9, 0x0d, 0x0e, 0x0f, 0x10, 0xe5, 0x11, 0x12, 0xa0, 0x1d, 0x1e, 0xa9, 0xe8, 0xf5, 0x9b, 0x01, 0xad, 0xbb, 0xeb, 0xfa, 0x02, 0xa7, 0xe6, 0xe2, 0xe7, 0x03, 0x04, 0x05, 0x06, 0xe9, 0xf8, 0x07, 0xac, 0xef, 0xf0, 0x08, 0xed, 0xf6, 0xf9, 0x09, 0xf7, 0x0a, 0x0b, 0xae, 0x0c, 0xe3, 0x0d, 0xe5, 0xf4, 0x0e, 0x0f, 0xe4, 0x10, 0xec, 0x11, 0xe1, 0x12, 0x13, 0x14, 0x15, 0x16, 0xee, 0xf3, 0x17, 0x18, 0xf2, 0xa0, 0x19, 0x1a, 0x1b, 0x1c, 0x09, 0x0a, 0xae, 0x9b, 0xec, 0x01, 0xf5, 0x02, 0xf4, 0xe6, 0x03, 0xe1, 0xe5, 0xe9, 0x04, 0xf2, 0xef, 0x05, 0x06, 0x07, 0xa0, 0x08, 0x0e, 0x0f, 0xad, 0xe7, 0x9b, 0xa7, 0xf9, 0x01, 0xec, 0x02, 0xac, 0xf2, 0x03, 0xae, 0xf3, 0xf5, 0x04, 0x05, 0xef, 0x06, 0x07, 0xe9, 0xe1, 0x08, 0x09, 0xe8, 0x0a, 0x0b, 0xe5, 0x0c, 0xa0, 0x0d, 0x0d, 0x0e, 0xa7, 0xac, 0xf3, 0xad, 0x01, 0x02, 0x9b, 0xf9, 0xf5, 0xae, 0x03, 0xee, 0x04, 0xf2, 0x05, 0x06, 0xf4, 0x07, 0x08, 0x09, 0xef, 0xe1, 0xa0, 0x0a, 0xe9, 0x0b, 0x0c, 0xe5, 0x14, 0x15, 0xac, 0xe2, 0xf8, 0x9b, 0xae, 0xfa, 0x01, 0xeb, 0x02, 0xa0, 0x03, 0x04, 0xf0, 0x05, 0x06, 0xe6, 0xf6, 0x07, 0xe4, 0xed, 0xe7, 0x08, 0xe1, 0xef, 0xf2, 0x09, 0x0a, 0x0b, 0xec, 0x0c, 0xe5, 0xe3, 0x0d, 0xf4, 0x0e, 0xf3, 0x0f, 0x10, 0x11, 0xee, 0x12, 0x13, 0x03, 0xef, 0x9b, 0xe1, 0xe5, 0xf5, 0x01, 0x02, 0x08, 0x09, 0xec, 0xf9, 0xa7, 0xee, 0x01, 0xac, 0x9b, 0xae, 0x02, 0x03, 0x04, 0xf3, 0x05, 0xe9, 0x06, 0xa0, 0x07, 0xe5, 0x16, 0x17, 0xa7, 0xad, 0xee, 0xe3, 0xeb, 0xf2, 0x9b, 0xe2, 0x01, 0x02, 0xf5, 0x03, 0xf4, 0xac, 0x04, 0x05, 0xe6, 0xed, 0xf6, 0x06, 0xae, 0xf0, 0x07, 0x08, 0xf3, 0x09, 0x0a, 0xe4, 0x0b, 0x0c, 0xf9, 0x0d, 0xef, 0x0e, 0xe1, 0x0f, 0x10, 0xe9, 0xec, 0x11, 0xa0, 0xe5, 0x12, 0x13, 0x14, 0x15, 0x0c, 0x0d, 0xa7, 0xbb, 0x9b, 0x01, 0xf9, 0xae, 0xe2, 0x02, 0xed, 0xf3, 0x03, 0xf5, 0xef, 0xf0, 0x04, 0x05, 0xe9, 0x06, 0x07, 0x08, 0x09, 0xa0, 0xe1, 0xe5, 0x0a, 0x0b, 0x19, 0x1a, 0xad, 0xbb, 0xe2, 0xea, 0xed, 0xf2, 0xfa, 0xe6, 0xec, 0x01, 0x02, 0x03, 0x9b, 0xf5, 0x04, 0xa7, 0xf6, 0xf9, 0x05, 0x06, 0xeb, 0xef, 0x07, 0x08, 0x09, 0x0a, 0xac, 0x0b, 0x0c, 0xe3, 0xae, 0x0d, 0xee, 0xe9, 0x0e, 0xe1, 0x0f, 0xf3, 0x10, 0x11, 0xf4, 0x12, 0xe7, 0xe5, 0x13, 0x14, 0xe4, 0x15, 0x16, 0x17, 0xa0, 0x18, 0x1a, 0x1b, 0xc2, 0x9b, 0xad, 0xac, 0xf8, 0x01, 0xae, 0x02, 0x03, 0xe5, 0xe7, 0xe8, 0xf9, 0xe9, 0xeb, 0x04, 0xe3, 0xe1, 0x05, 0xf6, 0x06, 0xe4, 0x07, 0xe2, 0xf0, 0x08, 0x09, 0xf3, 0xf4, 0xf7, 0xef, 0x0a, 0x0b, 0x0c, 0x0d, 0xec, 0x0e, 0x0f, 0x10, 0xf5, 0xed, 0x11, 0xe6, 0xa0, 0x12, 0xf2, 0x13, 0x14, 0x15, 0xee, 0x16, 0x17, 0x18, 0x19, 0x0e, 0x0f, 0xad, 0xed, 0xf9, 0x9b, 0xae, 0x01, 0xf3, 0x02, 0x03, 0xf5, 0xf4, 0xf0, 0x04, 0xef, 0x05, 0xe9, 0x06, 0xe8, 0xa0, 0xe1, 0xec, 0x07, 0xf2, 0x08, 0xe5, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x9b, 0xf5, 0x19, 0x1a, 0xa9, 0xbb, 0xf6, 0xe6, 0x01, 0x9b, 0xad, 0xe2, 0xf0, 0x02, 0xa7, 0x03, 0x04, 0x05, 0xf5, 0xe3, 0xac, 0xe7, 0xf2, 0x06, 0xeb, 0x07, 0xec, 0xed, 0xee, 0xf9, 0x08, 0xae, 0x09, 0x0a, 0xe4, 0x0b, 0x0c, 0xf4, 0x0d, 0xf3, 0x0e, 0x0f, 0x10, 0xe1, 0xef, 0x11, 0xe9, 0x12, 0x13, 0xe5, 0x14, 0xa0, 0x15, 0x16, 0x17, 0x18, 0xa0, 0x16, 0xa2, 0xa7, 0xe2, 0xeb, 0xed, 0xee, 0x9b, 0xf7, 0x01, 0x02, 0x03, 0xbb, 0xf9, 0xf0, 0x04, 0x05, 0xec, 0x06, 0x07, 0x08, 0xf5, 0xe1, 0x09, 0xac, 0xe3, 0x0a, 0xe8, 0x0b, 0xe9, 0x0c, 0xef, 0xf3, 0xae, 0x0d, 0x0e, 0xe5, 0x0f, 0x10, 0x11, 0xf4, 0x12, 0x13, 0x14, 0x15, 0x14, 0x15, 0xbb, 0xe2, 0xad, 0xed, 0x01, 0x9b, 0xa7, 0xe3, 0xac, 0xec, 0xee, 0x02, 0xf7, 0x03, 0x04, 0xf9, 0x05, 0x06, 0x07, 0x08, 0xf4, 0xae, 0xf5, 0x09, 0x0a, 0xf2, 0xe1, 0xf3, 0x0b, 0x0c, 0x0d, 0xe9, 0x0e, 0x0f, 0xef, 0xe5, 0x10, 0xa0, 0xe8, 0x11, 0x12, 0x13, 0x11, 0x12, 0xef, 0xf6, 0x9b, 0xeb, 0xf9, 0x01, 0xa0, 0xe2, 0x02, 0xe1, 0x03, 0xed, 0x04, 0xe3, 0xe9, 0x05, 0xe4, 0xe5, 0xe7, 0x06, 0xec, 0xf0, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xf3, 0x0c, 0xf4, 0xee, 0x0d, 0xf2, 0x0e, 0x0f, 0x10, 0x05, 0xe5, 0xf3, 0xf9, 0x9b, 0x01, 0xef, 0x02, 0x03, 0xe1, 0x04, 0xe9, 0x0a, 0x0b, 0xae, 0x9b, 0xec, 0xed, 0x01, 0x02, 0xf3, 0xee, 0xf2, 0x03, 0xe5, 0x04, 0xe8, 0xa0, 0xe1, 0x05, 0xef, 0x06, 0x07, 0x08, 0xe9, 0x09, 0x05, 0x06, 0xa0, 0xac, 0xad, 0xf4, 0xe9, 0x01, 0x02, 0xe1, 0xe5, 0x03, 0x9b, 0x04, 0x11, 0xa0, 0xbf, 0xe1, 0xe2, 0xe6, 0xed, 0xe4, 0xe9, 0xf7, 0xa7, 0x01, 0x02, 0xbb, 0x03, 0x04, 0xec, 0x05, 0x9b, 0xee, 0x06, 0xef, 0x07, 0xac, 0xe5, 0xf3, 0x08, 0x09, 0x0a, 0xae, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x06, 0x07, 0xa0, 0xae, 0xe1, 0xe5, 0xec, 0xfa, 0x9b, 0xef, 0xe9, 0x01, 0x02, 0x03, 0x04, 0x05, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b }; void DvbPmtFilter::processSection(const char *data, int size) { unsigned char tableId = data[0]; if (tableId != 0x02) { return; } DvbPmtSection pmtSection(data, size); if (!pmtSection.isValid() || (pmtSection.programNumber() != programNumber)) { return; } if ((size == lastPmtSectionData.size()) && (memcmp(data, lastPmtSectionData.constData(), size) == 0)) { return; } lastPmtSectionData = pmtSection.toByteArray(); emit pmtSectionChanged(lastPmtSectionData); } void DvbSectionGenerator::initPat(int transportStreamId, int programNumber, int pmtPid) { Q_ASSERT((pmtPid >= 0) && (pmtPid <= 0x1fff)); char *data = startSection(16); data[0] = 0x00; data[3] = char(transportStreamId >> 8); data[4] = char(transportStreamId); data[8] = char(programNumber >> 8); data[9] = char(programNumber); data[10] = 0xe0 | char(pmtPid >> 8); data[11] = char(pmtPid); endSection(16, 0x00); } void DvbSectionGenerator::initPmt(int pmtPid, const DvbPmtSection §ion, const QList &pids) { Q_ASSERT(section.isValid()); char *data = startSection(section.getSectionLength()); DvbPmtSectionEntry entry = section.entries(); memcpy(data, section.getData(), entry.getData() - section.getData()); int size = int(entry.getData() - section.getData()); while (entry.isValid()) { if (pids.contains(entry.pid())) { memcpy(data + size, entry.getData(), entry.getLength()); size += entry.getLength(); } entry.advance(); } endSection(size + 4, pmtPid); } QByteArray DvbSectionGenerator::generatePackets() { char *data = packets.data(); for (int i = 3; i < packets.size(); i += 188) { data[i] = (data[i] & 0xf0) | char(continuityCounter); continuityCounter = (continuityCounter + 1) & 0x0f; } return packets; } char *DvbSectionGenerator::startSection(int sectionLength) { Q_ASSERT((sectionLength >= 4) && (sectionLength <= 0x1002)); packets.resize(((sectionLength / 184) + 1) * 188); return packets.data() + 5; } void DvbSectionGenerator::endSection(int sectionLength, int pid) { Q_ASSERT((sectionLength >= 4) && (sectionLength <= 0x1002)); Q_ASSERT((pid >= 0) && (pid <= 0x1fff)); packets.resize(((sectionLength / 184) + 1) * 188); char *data = packets.data(); data[0] = 0x47; data[1] = 0x40 | char(pid >> 8); data[2] = char(pid); data[3] = 0x10; data[4] = 0x00; data[6] = 0xb0 | char((sectionLength - 3) >> 8); data[7] = char(sectionLength - 3); data[10] = 0xc1 | char(versionNumber << 1); data[11] = 0x00; data[12] = 0x00; int size = sectionLength + 5; unsigned int crc32 = 0xffffffff; for (int i = 5; i < (size - 4); ++i) { unsigned char byte = data[i]; crc32 = (crc32 << 8) ^ DvbStandardSection::crc32Table[(crc32 >> 24) ^ byte]; } data[size - 4] = char(crc32 >> 24); data[size - 3] = char(crc32 >> 16); data[size - 2] = char(crc32 >> 8); data[size - 1] = char(crc32); for (int i = 188; i < size; i += 188) { // split the section into multiple packets if necessary memmove(data + i + 4, data + i, size - i); data[i] = 0x47; data[i + 1] = char(pid >> 8); data[i + 2] = char(pid); data[i + 3] = 0x10; // continuity counter is filled out in generatePackets() size += 4; } // pad the unused bytes memset(data + size, 0xff, packets.size() - size); // increment version number versionNumber = (versionNumber + 1) & 0x1f; } DvbPmtParser::DvbPmtParser(const DvbPmtSection §ion) : videoPid(-1), teletextPid(-1) { for (DvbPmtSectionEntry entry = section.entries(); entry.isValid(); entry.advance()) { QString streamLanguage; QString subtitleLanguage; bool teletextPresent = false; bool ac3Present = false; for (DvbDescriptor descriptor = entry.descriptors(); descriptor.isValid(); descriptor.advance()) { switch (descriptor.descriptorTag()) { case 0x0a: { DvbLanguageDescriptor languageDescriptor(descriptor); if (!languageDescriptor.isValid()) { break; } // ISO 8859-1 equals to unicode range 0x0000 - 0x00ff streamLanguage.clear(); streamLanguage.append(QChar(languageDescriptor.languageCode1())); streamLanguage.append(QChar(languageDescriptor.languageCode2())); streamLanguage.append(QChar(languageDescriptor.languageCode3())); break; } case 0x56: teletextPresent = true; break; case 0x59: { DvbSubtitleDescriptor subtitleDescriptor(descriptor); if (!subtitleDescriptor.isValid()) { break; } if ((subtitleDescriptor.subtitleType() >= 0x01) && (subtitleDescriptor.subtitleType() <= 0x03)) { // FIXME how to deal with vbi and teletext subtitles? qCInfo(logDvbSi, "Unsupported subtitle found: VBI/teletext (%d)", subtitleDescriptor.subtitleType()); } // ISO 8859-1 equals to unicode range 0x0000 - 0x00ff subtitleLanguage.clear(); subtitleLanguage.append(QChar(subtitleDescriptor.languageCode1())); subtitleLanguage.append(QChar(subtitleDescriptor.languageCode2())); subtitleLanguage.append(QChar(subtitleDescriptor.languageCode3())); break; } case 0x6a: case 0x7a: ac3Present = true; break; } } // Updated with Table 2-34 of ISO/IEC 13818-1:2018 / ITU-T H.222.0 2017 // and with https://www.wikiwand.com/en/Program-specific_information // (as private IDs are not at ITU/ISO/IEC specs switch (entry.streamType()) { case 0x01: // ISO/IEC 11172-2 (MPEG-1) video case 0x02: // ITU-T H.262 (MPEG2) video or ISO/IEC 11172-2 case 0x10: // ISO/IEC 14496-2 (MPEG-4) video case 0x1b: // ITU-T H.264 video case 0x1e: // ISO/IEC 23002-3 (MPEG-4 auxiliary video) case 0x1f: // ITU-T H.264 Annex G (MPEG-4 SVC sub-bitstream on AVC) case 0x20: // ITU-T H.264 Annex H (MPEG-4 MVC sub-bitstream on AVC) case 0x21: // ITU-T T.800 (JPEG 2000 video) case 0x22: // ITU-T H.262 additional view for 3D case 0x23: // ITU-T H.264 additional view for 3D case 0x24: // ITU-T H.265 or HEVC sub-bitstream case 0x25: // ITU-T H.265 Annex A - HEVC video stream or subset case 0x26: // ITU-T H.264 Annex I - MVCD substream on HEVC case 0x28: // ITU-T H.265 Annex G - HEVC enhancemeng sub-partition case 0x29: // ITU-T H.265 Annex G - HEVC temporal enhancement case 0x2a: // ITU-T H.265 Annex H - HEVC enhancement case 0x2b: // ITU-T H.265 Annex H - HEVC temporal enhancement case 0x42: // CAVS video // User-private video streams case 0x80: // MPEG-2 MOTO video case 0xd1: // Dirac (Ultra HD video) if (videoPid < 0) { videoPid = entry.pid(); } else { qCInfo(logDvbSi, "More than one video PID"); } break; case 0x03: // ISO/IEC 11172-3 (MPEG1) audio case 0x04: // ISO/IEC 13818-3 (MPEG2) audio case 0x07: // ISO/IEC 13522 MHEG - DTS and DTS-HD Audio case 0x0f: // ISO/IEC 13818-7 Audio with ADTS transport syntax case 0x11: // ISO/IEC 14496-3 Audio (AAC / LATM) case 0x1c: // ISO/IEC 14496-3 Audio, without additional transport syntax case 0x2d: // ISO/IEC 23008-3 Audio with MHAS – main stream case 0x2e: // ISO/IEC 23008-3 Audio with MHAS – auxiliary stream // User-private audio streams case 0x81: // AC-3 audio (ATSC specific) case 0x83: // TrueHD lossless audio case 0x84: // SDDS case 0x85: // DTS on HDMV case 0x86: // DTS 8 channel case 0x87: // enhanced AC-3 audio (ATSC specific) case 0x8a: // DTS case 0x91: // A52 VLS case 0x94: // SDDS audioPids.append(qMakePair(entry.pid(), streamLanguage)); break; case 0x06: // private data - can be teletext, subtitle, ac3 or something else if (teletextPresent) { if (teletextPid < 0) { teletextPid = entry.pid(); } else { qCInfo(logDvbSi, "More than one teletext PID"); } } if (!subtitleLanguage.isEmpty()) { subtitlePids.append(qMakePair(entry.pid(), subtitleLanguage)); if (teletextPresent) { qCInfo(logDvbSi, "Subtitle and teletext on the same PID"); } } if (ac3Present) { audioPids.append(qMakePair(entry.pid(), streamLanguage)); } break; default: if (!subtitleLanguage.isEmpty()) { qCInfo(logDvbSi, "Subtitle with unexpected stream type found"); } if (teletextPresent) { qCInfo(logDvbSi, "Teletext with unexpected stream type found"); } break; } } } void AtscEitSectionEntry::initEitSectionEntry(const char *data, int size) { if (size < 12) { if (size != 0) { qCInfo(logDvbSi, "Invalid entry"); } initSectionData(); return; } titleLength = quint8(data[9]); if (titleLength > (size - 12)) { qCInfo(logDvbSi, "Adjusting length"); titleLength = (size - 12); } // too ugly to be automatically generated int entryLength = ((((quint8(data[10 + titleLength]) & 0xf) << 8) | quint8(data[11 + titleLength])) + 12 + titleLength); if (entryLength > size) { qCInfo(logDvbSi, "Adjusting length"); entryLength = size; } initSectionData(data, entryLength, size); } // everything below this line is automatically generated void DvbPatSectionEntry::initPatSectionEntry(const char *data, int size) { if (size < 4) { if (size != 0) { qCWarning(logDvbSi, "Invalid entry at descriptor"); } initSectionData(); return; } initSectionData(data, 4, size); } void DvbPmtSectionEntry::initPmtSectionEntry(const char *data, int size) { if (size < 5) { if (size != 0) { qCWarning(logDvbSi, "Invalid entry at descriptor"); } initSectionData(); return; } int entryLength = ((((quint8(data[3]) & 0xf) << 8) | quint8(data[4])) + 5); if (entryLength > size) { qCWarning(logDvbSi, "Adjusting length on descriptor"); entryLength = size; } initSectionData(data, entryLength, size); } void DvbSdtSectionEntry::initSdtSectionEntry(const char *data, int size) { if (size < 5) { if (size != 0) { qCWarning(logDvbSi, "Invalid entry at descriptor"); } initSectionData(); return; } int entryLength = ((((quint8(data[3]) & 0xf) << 8) | quint8(data[4])) + 5); if (entryLength > size) { qCWarning(logDvbSi, "Adjusting length on descriptor"); entryLength = size; } initSectionData(data, entryLength, size); } void DvbEitSectionEntry::initEitSectionEntry(const char *data, int size) { if (size < 12) { if (size != 0) { qCWarning(logDvbSi, "Invalid entry at descriptor"); } initSectionData(); return; } int entryLength = ((((quint8(data[10]) & 0xf) << 8) | quint8(data[11])) + 12); if (entryLength > size) { qCWarning(logDvbSi, "Adjusting length on descriptor"); entryLength = size; } initSectionData(data, entryLength, size); } void DvbEitContentEntry::initEitContentEntry(const char *data, int size) { if (size < 2) { if (size != 0) { qCWarning(logDvbSi, "Invalid entry at descriptor"); } initSectionData(); return; } initSectionData(data, 2, size); } void DvbParentalRatingEntry::initParentalRatingEntry(const char *data, int size) { if (size < 4) { if (size != 0) { qCWarning(logDvbSi, "Invalid entry at descriptor"); } initSectionData(); return; } initSectionData(data, 4, size); } void DvbNitSectionEntry::initNitSectionEntry(const char *data, int size) { if (size < 6) { if (size != 0) { qCWarning(logDvbSi, "Invalid entry at descriptor"); } initSectionData(); return; } int entryLength = ((((quint8(data[4]) & 0xf) << 8) | quint8(data[5])) + 6); if (entryLength > size) { qCWarning(logDvbSi, "Adjusting length on descriptor"); entryLength = size; } initSectionData(data, entryLength, size); } void AtscMgtSectionEntry::initMgtSectionEntry(const char *data, int size) { if (size < 11) { if (size != 0) { qCWarning(logDvbSi, "Invalid entry at descriptor"); } initSectionData(); return; } int entryLength = ((((quint8(data[9]) & 0xf) << 8) | quint8(data[10])) + 11); if (entryLength > size) { qCWarning(logDvbSi, "Adjusting length on descriptor"); entryLength = size; } initSectionData(data, entryLength, size); } void AtscVctSectionEntry::initVctSectionEntry(const char *data, int size) { if (size < 32) { if (size != 0) { qCWarning(logDvbSi, "Invalid entry at descriptor"); } initSectionData(); return; } int entryLength = ((((quint8(data[30]) & 0x3) << 8) | quint8(data[31])) + 32); if (entryLength > size) { qCWarning(logDvbSi, "Adjusting length on descriptor"); entryLength = size; } initSectionData(data, entryLength, size); } DvbLanguageDescriptor::DvbLanguageDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor) { if (getLength() < 6) { qCWarning(logDvbSi, "Invalid descriptor"); initSectionData(); return; } } DvbSubtitleDescriptor::DvbSubtitleDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor) { if (getLength() < 10) { qCWarning(logDvbSi, "Invalid descriptor"); initSectionData(); return; } } DvbServiceDescriptor::DvbServiceDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor) { if (getLength() < 5) { qCWarning(logDvbSi, "Invalid descriptor"); initSectionData(); return; } providerNameLength = at(3); if (providerNameLength > (getLength() - 5)) { qCWarning(logDvbSi, "Adjusting length on descriptor"); providerNameLength = (getLength() - 5); } serviceNameLength = at(4 + providerNameLength); if (serviceNameLength > (getLength() - (5 + providerNameLength))) { qCWarning(logDvbSi, "Adjusting length on descriptor"); serviceNameLength = (getLength() - (5 + providerNameLength)); } } DvbShortEventDescriptor::DvbShortEventDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor) { if (getLength() < 7) { qCWarning(logDvbSi, "Invalid descriptor"); initSectionData(); return; } eventNameLength = at(5); if (eventNameLength > (getLength() - 7)) { qCWarning(logDvbSi, "Adjusting length on descriptor"); eventNameLength = (getLength() - 7); } textLength = at(6 + eventNameLength); if (textLength > (getLength() - (7 + eventNameLength))) { qCWarning(logDvbSi, "Adjusting length on descriptor"); textLength = (getLength() - (7 + eventNameLength)); } } DvbExtendedEventDescriptor::DvbExtendedEventDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor) { if (getLength() < 8) { qCWarning(logDvbSi, "Invalid descriptor"); initSectionData(); return; } itemsLength = at(6); if (itemsLength > (getLength() - 8)) { qCWarning(logDvbSi, "Adjusting length on descriptor"); itemsLength = (getLength() - 8); } textLength = at(7 + itemsLength); if (textLength > (getLength() - (8 + itemsLength))) { qCWarning(logDvbSi, "Adjusting length on descriptor"); textLength = (getLength() - (8 + itemsLength)); } } DvbContentDescriptor::DvbContentDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor) { if (getLength() < 2) { qCWarning(logDvbSi, "Invalid descriptor"); initSectionData(); return; } } DvbParentalRatingDescriptor::DvbParentalRatingDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor) { if (getLength() < 2) { qCWarning(logDvbSi, "Invalid descriptor"); initSectionData(); return; } } DvbCableDescriptor::DvbCableDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor) { if (getLength() < 13) { qCWarning(logDvbSi, "Invalid descriptor"); initSectionData(); return; } } DvbSatelliteDescriptor::DvbSatelliteDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor) { if (getLength() < 13) { qCWarning(logDvbSi, "Invalid descriptor"); initSectionData(); return; } } DvbTerrestrialDescriptor::DvbTerrestrialDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor) { if (getLength() < 13) { qCWarning(logDvbSi, "Invalid descriptor"); initSectionData(); return; } } IsdbTerrestrialDescriptor::IsdbTerrestrialDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor) { if (getLength() < 4) { qCWarning(logDvbSi, "Invalid descriptor"); initSectionData(); return; } } AtscChannelNameDescriptor::AtscChannelNameDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor) { if (getLength() < 2) { qCWarning(logDvbSi, "Invalid descriptor"); initSectionData(); return; } } void DvbPatSection::initPatSection(const char *data, int size) { if (size < 12) { initSectionData(); return; } initStandardSection(data, size); } void DvbPmtSection::initPmtSection(const char *data, int size) { if (size < 16) { initSectionData(); return; } initStandardSection(data, size); descriptorsLength = ((at(10) & 0xf) << 8) | at(11); if (descriptorsLength > (getLength() - 16)) { qCWarning(logDvbSi, "Adjusting length on descriptor"); descriptorsLength = (getLength() - 16); } } void DvbSdtSection::initSdtSection(const char *data, int size) { if (size < 15) { initSectionData(); return; } initStandardSection(data, size); } void DvbEitSection::initEitSection(const char *data, int size) { if (size < 18) { initSectionData(); return; } initStandardSection(data, size); } void DvbNitSection::initNitSection(const char *data, int size) { if (size < 16) { initSectionData(); return; } initStandardSection(data, size); descriptorsLength = ((at(8) & 0xf) << 8) | at(9); if (descriptorsLength > (getLength() - 16)) { qCWarning(logDvbSi, "Adjusting length on descriptor"); descriptorsLength = (getLength() - 16); } entriesLength = ((at(10 + descriptorsLength) & 0xf) << 8) | at(11 + descriptorsLength); if (entriesLength > (getLength() - (16 + descriptorsLength))) { qCWarning(logDvbSi, "Adjusting length on descriptor"); entriesLength = (getLength() - (16 + descriptorsLength)); } } void AtscMgtSection::initMgtSection(const char *data, int size) { if (size < 17) { initSectionData(); return; } initStandardSection(data, size); } void AtscVctSection::initVctSection(const char *data, int size) { if (size < 14) { initSectionData(); return; } initStandardSection(data, size); } void AtscEitSection::initEitSection(const char *data, int size) { if (size < 14) { initSectionData(); return; } initStandardSection(data, size); } void AtscEttSection::initEttSection(const char *data, int size) { if (size < 17) { initSectionData(); return; } initStandardSection(data, size); } diff --git a/src/dvb/dvbsi.h b/src/dvb/dvbsi.h index e047b01..8066324 100644 --- a/src/dvb/dvbsi.h +++ b/src/dvb/dvbsi.h @@ -1,1391 +1,1391 @@ /* * dvbsi.h * * Copyright (C) 2008-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DVBSI_H #define DVBSI_H #include #include #include #include #include #include "dvbbackenddevice.h" class DvbPmtSection; class DvbSectionData { public: bool isValid() const { return (length > 0); } unsigned char at(int index) const { return data[index]; } const char *getData() const { return data; } int getLength() const { return length; } QByteArray toByteArray() const { return QByteArray(data, length); } protected: DvbSectionData() { } ~DvbSectionData() { } void initSectionData() { data = NULL; length = 0; size = 0; } void initSectionData(const char *data_, int length_, int size_) { data = data_; length = length_; size = size_; } int getSize() const { return size; } private: const char *data; int length; int size; }; class DvbSection : public DvbSectionData { public: int getSectionLength() const { return getLength(); } int tableId() const { return at(0); } bool isStandardSection() const { return (at(1) & 0x80) != 0; } protected: DvbSection() { } ~DvbSection() { } void initSection(const char *data, int size); private: Q_DISABLE_COPY(DvbSection) }; class DvbStandardSection : public DvbSection { public: int tableIdExtension() const { return (at(3) << 8 | at(4)); } int versionNumber() const { return (at(5) >> 1) & ((1 << 5) - 1); } bool currentNextIndicator() const { return (at(5) & 0x01) != 0; } int sectionNumber() const { return at(6); } int lastSectionNumber() const { return at(7); } static int verifyCrc32(const char *data, int size); static const unsigned int crc32Table[]; protected: DvbStandardSection() { } ~DvbStandardSection() { } void initStandardSection(const char *data, int size); private: Q_DISABLE_COPY(DvbStandardSection) }; class DvbSiText { public: static QString convertText(const char *data, int size); static void setOverride6937(bool override); private: enum TextEncoding { Iso6937 = 0, Iso8859_1 = 1, Iso8859_2 = 2, Iso8859_3 = 3, Iso8859_4 = 4, Iso8859_5 = 5, Iso8859_6 = 6, Iso8859_7 = 7, Iso8859_8 = 8, Iso8859_9 = 9, Iso8859_10 = 10, Iso8859_11 = 11, Iso8859_13 = 12, Iso8859_14 = 13, Iso8859_15 = 14, Iso2022_kr = 15, Iso10646_ucs2 = 16, Gb2312 = 17, Utf_16be = 18, Utf_8 = 19, EncodingTypeMax = 19 }; static QTextCodec *codecTable[EncodingTypeMax + 1]; static bool override6937; }; class DvbDescriptor : public DvbSectionData { public: DvbDescriptor(const char *data, int size) { initDescriptor(data, size); } ~DvbDescriptor() { } int descriptorTag() const { return at(0); } void advance() { initDescriptor(getData() + getLength(), getSize() - getLength()); } static int bcdToInt(unsigned int bcd, int multiplier) { int value = 0; while (bcd != 0) { value += (bcd & 0xf) * multiplier; multiplier *= 10; bcd >>= 4; } return value; } private: void initDescriptor(const char *data, int size); }; // ATSC "Multiple String Structure". See A/65C Section 6.10 class AtscPsipText { public: static QString convertText(const char *data, int size); private: static QString interpretTextData(const char *data, unsigned int len, unsigned int mode); }; // ATSC Huffman compressed string support, conforming to A/65C Annex C class AtscHuffmanString { public: static QString convertText(const char *data_, int size, int table); private: AtscHuffmanString(const char *data_, int size, int table); ~AtscHuffmanString(); bool hasBits(); unsigned char getBit(); unsigned char getByte(); void decompress(); const char *data; int bitsLeft; QString result; const unsigned short *offsets; const unsigned char *tableBase; static const unsigned short Huffman1Offsets[128]; static const unsigned char Huffman1Tables[]; static const unsigned short Huffman2Offsets[128]; static const unsigned char Huffman2Tables[]; }; class DvbPmtFilter : public QObject, public DvbSectionFilter { Q_OBJECT public: DvbPmtFilter() : programNumber(-1) { } ~DvbPmtFilter() { } void setProgramNumber(int programNumber_) { programNumber = programNumber_; } signals: void pmtSectionChanged(const QByteArray &pmtSectionData); private: - void processSection(const char *data, int size); + void processSection(const char *data, int size) override; int programNumber; QByteArray lastPmtSectionData; }; class DvbSectionGenerator { public: DvbSectionGenerator() : versionNumber(0), continuityCounter(0) { } ~DvbSectionGenerator() { } void initPat(int transportStreamId, int programNumber, int pmtPid); void initPmt(int pmtPid, const DvbPmtSection §ion, const QList &pids); void reset() { packets.clear(); versionNumber = 0; continuityCounter = 0; } QByteArray generatePackets(); private: char *startSection(int sectionLength); void endSection(int sectionLength, int pid); QByteArray packets; int versionNumber; int continuityCounter; }; class DvbPmtParser { public: explicit DvbPmtParser(const DvbPmtSection §ion); ~DvbPmtParser() { } int videoPid; QList > audioPids; // QString = language code (may be empty) QList > subtitlePids; // QString = language code int teletextPid; }; class AtscEitSectionEntry : public DvbSectionData { public: AtscEitSectionEntry(const char *data, int size) { initEitSectionEntry(data, size); } ~AtscEitSectionEntry() { } void advance() { initEitSectionEntry(getData() + getLength(), getSize() - getLength()); } int eventId() const { return ((at(0) & 0x3f) << 8) | at(1); } int startTime() const { return (at(2) << 24) | (at(3) << 16) | (at(4) << 8) | at(5); } int duration() const { return ((at(6) & 0xf) << 16) | (at(7) << 8) | at(8); } QString title() const { return AtscPsipText::convertText(getData() + 10, titleLength); } private: void initEitSectionEntry(const char *data, int size); int titleLength; }; // everything below this line is automatically generated class DvbPatSectionEntry : public DvbSectionData { public: DvbPatSectionEntry(const char *data, int size) { initPatSectionEntry(data, size); } ~DvbPatSectionEntry() { } void advance() { initPatSectionEntry(getData() + getLength(), getSize() - getLength()); } int programNumber() const { return (at(0) << 8) | at(1); } int pid() const { return ((at(2) & 0x1f) << 8) | at(3); } private: void initPatSectionEntry(const char *data, int size); }; class DvbPmtSectionEntry : public DvbSectionData { public: DvbPmtSectionEntry(const char *data, int size) { initPmtSectionEntry(data, size); } ~DvbPmtSectionEntry() { } void advance() { initPmtSectionEntry(getData() + getLength(), getSize() - getLength()); } int streamType() const { return at(0); } int pid() const { return ((at(1) & 0x1f) << 8) | at(2); } DvbDescriptor descriptors() const { return DvbDescriptor(getData() + 5, getLength() - 5); } private: void initPmtSectionEntry(const char *data, int size); }; class DvbSdtSectionEntry : public DvbSectionData { public: DvbSdtSectionEntry(const char *data, int size) { initSdtSectionEntry(data, size); } ~DvbSdtSectionEntry() { } void advance() { initSdtSectionEntry(getData() + getLength(), getSize() - getLength()); } int serviceId() const { return (at(0) << 8) | at(1); } bool isScrambled() const { return ((at(3) & 0x10) != 0); } DvbDescriptor descriptors() const { return DvbDescriptor(getData() + 5, getLength() - 5); } private: void initSdtSectionEntry(const char *data, int size); }; class DvbEitSectionEntry : public DvbSectionData { public: DvbEitSectionEntry(const char *data, int size) { initEitSectionEntry(data, size); } ~DvbEitSectionEntry() { } void advance() { initEitSectionEntry(getData() + getLength(), getSize() - getLength()); } int startDate() const { return (at(2) << 8) | at(3); } int startTime() const { return (at(4) << 16) | (at(5) << 8) | at(6); } int duration() const { return (at(7) << 16) | (at(8) << 8) | at(9); } DvbDescriptor descriptors() const { return DvbDescriptor(getData() + 12, getLength() - 12); } private: void initEitSectionEntry(const char *data, int size); }; class DvbEitContentEntry : public DvbSectionData { public: DvbEitContentEntry(const char *data, int size) { initEitContentEntry(data, size); } ~DvbEitContentEntry() { } void advance() { initEitContentEntry(getData() + getLength(), getSize() - getLength()); } int contentNibbleLevel1() const { return (at(0) >> 4); } int contentNibbleLevel2() const { return (at(0) & 0xf); } int userByte() const { return at(1); } private: void initEitContentEntry(const char *data, int size); }; class DvbParentalRatingEntry : public DvbSectionData { public: DvbParentalRatingEntry(const char *data, int size) { initParentalRatingEntry(data, size); } ~DvbParentalRatingEntry() { } void advance() { initParentalRatingEntry(getData() + getLength(), getSize() - getLength()); } int languageCode1() const { return at(0); } int languageCode2() const { return at(1); } int languageCode3() const { return at(2); } int rating() const { return at(3); } private: void initParentalRatingEntry(const char *data, int size); }; class DvbNitSectionEntry : public DvbSectionData { public: DvbNitSectionEntry(const char *data, int size) { initNitSectionEntry(data, size); } ~DvbNitSectionEntry() { } void advance() { initNitSectionEntry(getData() + getLength(), getSize() - getLength()); } DvbDescriptor descriptors() const { return DvbDescriptor(getData() + 6, getLength() - 6); } private: void initNitSectionEntry(const char *data, int size); }; class AtscMgtSectionEntry : public DvbSectionData { public: AtscMgtSectionEntry(const char *data, int size) { initMgtSectionEntry(data, size); } ~AtscMgtSectionEntry() { } void advance() { initMgtSectionEntry(getData() + getLength(), getSize() - getLength()); } int tableType() const { return (at(0) << 8) | at(1); } int pid() const { return ((at(2) & 0x1f) << 8) | at(3); } DvbDescriptor descriptors() const { return DvbDescriptor(getData() + 11, getLength() - 11); } private: void initMgtSectionEntry(const char *data, int size); }; class AtscVctSectionEntry : public DvbSectionData { public: AtscVctSectionEntry(const char *data, int size) { initVctSectionEntry(data, size); } ~AtscVctSectionEntry() { } void advance() { initVctSectionEntry(getData() + getLength(), getSize() - getLength()); } int shortName1() const { return (at(0) << 8) | at(1); } int shortName2() const { return (at(2) << 8) | at(3); } int shortName3() const { return (at(4) << 8) | at(5); } int shortName4() const { return (at(6) << 8) | at(7); } int shortName5() const { return (at(8) << 8) | at(9); } int shortName6() const { return (at(10) << 8) | at(11); } int shortName7() const { return (at(12) << 8) | at(13); } int majorNumber() const { return ((at(14) & 0xf) << 6) | (at(15) >> 2); } int minorNumber() const { return ((at(15) & 0x3) << 8) | at(16); } int programNumber() const { return (at(24) << 8) | at(25); } bool isScrambled() const { return ((at(26) & 0x20) != 0); } int sourceId() const { return (at(28) << 8) | at(29); } DvbDescriptor descriptors() const { return DvbDescriptor(getData() + 32, getLength() - 32); } private: void initVctSectionEntry(const char *data, int size); }; class DvbLanguageDescriptor : public DvbDescriptor { public: explicit DvbLanguageDescriptor(const DvbDescriptor &descriptor); ~DvbLanguageDescriptor() { } int languageCode1() const { return at(2); } int languageCode2() const { return at(3); } int languageCode3() const { return at(4); } private: Q_DISABLE_COPY(DvbLanguageDescriptor) }; class DvbSubtitleDescriptor : public DvbDescriptor { public: explicit DvbSubtitleDescriptor(const DvbDescriptor &descriptor); ~DvbSubtitleDescriptor() { } int languageCode1() const { return at(2); } int languageCode2() const { return at(3); } int languageCode3() const { return at(4); } int subtitleType() const { return at(5); } private: Q_DISABLE_COPY(DvbSubtitleDescriptor) }; class DvbServiceDescriptor : public DvbDescriptor { public: explicit DvbServiceDescriptor(const DvbDescriptor &descriptor); ~DvbServiceDescriptor() { } QString providerName() const { return DvbSiText::convertText(getData() + 4, providerNameLength); } QString serviceName() const { return DvbSiText::convertText(getData() + 5 + providerNameLength, serviceNameLength); } private: Q_DISABLE_COPY(DvbServiceDescriptor) int providerNameLength; int serviceNameLength; }; class DvbShortEventDescriptor : public DvbDescriptor { public: explicit DvbShortEventDescriptor(const DvbDescriptor &descriptor); ~DvbShortEventDescriptor() { } int languageCode1() const { return at(2); } int languageCode2() const { return at(3); } int languageCode3() const { return at(4); } QString eventName() const { return DvbSiText::convertText(getData() + 6, eventNameLength); } QString text() const { return DvbSiText::convertText(getData() + 7 + eventNameLength, textLength); } private: Q_DISABLE_COPY(DvbShortEventDescriptor) int eventNameLength; int textLength; }; class DvbExtendedEventDescriptor : public DvbDescriptor { public: explicit DvbExtendedEventDescriptor(const DvbDescriptor &descriptor); ~DvbExtendedEventDescriptor() { } int languageCode1() const { return at(3); } int languageCode2() const { return at(4); } int languageCode3() const { return at(5); } QString text() const { return DvbSiText::convertText(getData() + 8 + itemsLength, textLength); } private: Q_DISABLE_COPY(DvbExtendedEventDescriptor) int itemsLength; int textLength; }; class DvbContentDescriptor : public DvbDescriptor { public: explicit DvbContentDescriptor(const DvbDescriptor &descriptor); ~DvbContentDescriptor() { } DvbEitContentEntry contents() const { return DvbEitContentEntry(getData() + 2, getLength() - 2); } private: Q_DISABLE_COPY(DvbContentDescriptor) }; class DvbParentalRatingDescriptor : public DvbDescriptor { public: explicit DvbParentalRatingDescriptor(const DvbDescriptor &descriptor); ~DvbParentalRatingDescriptor() { } DvbParentalRatingEntry contents() const { return DvbParentalRatingEntry(getData() + 2, getLength() - 2); } private: Q_DISABLE_COPY(DvbParentalRatingDescriptor) }; class DvbCableDescriptor : public DvbDescriptor { public: explicit DvbCableDescriptor(const DvbDescriptor &descriptor); ~DvbCableDescriptor() { } int frequency() const { return (at(2) << 24) | (at(3) << 16) | (at(4) << 8) | at(5); } int modulation() const { return at(8); } int symbolRate() const { return (at(9) << 20) | (at(10) << 12) | (at(11) << 4) | (at(12) >> 4); } int fecRate() const { return (at(12) & 0xf); } private: Q_DISABLE_COPY(DvbCableDescriptor) }; class DvbSatelliteDescriptor : public DvbDescriptor { public: explicit DvbSatelliteDescriptor(const DvbDescriptor &descriptor); ~DvbSatelliteDescriptor() { } int frequency() const { return (at(2) << 24) | (at(3) << 16) | (at(4) << 8) | at(5); } int polarization() const { return ((at(8) & 0x7f) >> 5); } int rollOff() const { return ((at(8) & 0x1f) >> 3); } bool isDvbS2() const { return ((at(8) & 0x4) != 0); } int modulation() const { return (at(8) & 0x3); } int symbolRate() const { return (at(9) << 20) | (at(10) << 12) | (at(11) << 4) | (at(12) >> 4); } int fecRate() const { return (at(12) & 0xf); } private: Q_DISABLE_COPY(DvbSatelliteDescriptor) }; class DvbTerrestrialDescriptor : public DvbDescriptor { public: explicit DvbTerrestrialDescriptor(const DvbDescriptor &descriptor); ~DvbTerrestrialDescriptor() { } int frequency() const { return (at(2) << 24) | (at(3) << 16) | (at(4) << 8) | at(5); } int bandwidth() const { return (at(6) >> 5); } int constellation() const { return (at(7) >> 6); } int hierarchy() const { return ((at(7) & 0x3f) >> 3); } int fecRateHigh() const { return (at(7) & 0x7); } int fecRateLow() const { return (at(8) >> 5); } int guardInterval() const { return ((at(8) & 0x1f) >> 3); } int transmissionMode() const { return ((at(8) & 0x7) >> 1); } private: Q_DISABLE_COPY(DvbTerrestrialDescriptor) }; class IsdbTerrestrialDescriptor : public DvbDescriptor { public: explicit IsdbTerrestrialDescriptor(const DvbDescriptor &descriptor); ~IsdbTerrestrialDescriptor() { } int areaCode() const { return (at(2) << 4) | (at(3) >> 4); } int guardInterval() const { return ((at(3) & 0xf) >> 2); } int transmissionMode() const { return (at(3) & 0x3); } int frequencyLength() const { return (getLength() - 4) / 2; } int frequency(int idx) const { int pos = (idx * 2) + 4; return (at(pos) << 8) | at(pos + 1); } private: Q_DISABLE_COPY(IsdbTerrestrialDescriptor) }; class AtscChannelNameDescriptor : public DvbDescriptor { public: explicit AtscChannelNameDescriptor(const DvbDescriptor &descriptor); ~AtscChannelNameDescriptor() { } QString name() const { return AtscPsipText::convertText(getData() + 2, getLength() - 2); } private: Q_DISABLE_COPY(AtscChannelNameDescriptor) }; class DvbPatSection : public DvbStandardSection { public: DvbPatSection(const char *data, int size) { initPatSection(data, size); } explicit DvbPatSection(const QByteArray &byteArray) { initPatSection(byteArray.constData(), byteArray.size()); } ~DvbPatSection() { } int transportStreamId() const { return (at(3) << 8) | at(4); } DvbPatSectionEntry entries() const { return DvbPatSectionEntry(getData() + 8, getLength() - 12); } private: Q_DISABLE_COPY(DvbPatSection) void initPatSection(const char *data, int size); }; class DvbPmtSection : public DvbStandardSection { public: DvbPmtSection(const char *data, int size) { initPmtSection(data, size); } explicit DvbPmtSection(const QByteArray &byteArray) { initPmtSection(byteArray.constData(), byteArray.size()); } ~DvbPmtSection() { } int programNumber() const { return (at(3) << 8) | at(4); } int pcrPid() const { return ((at(8) & 0x1f) << 8) | at(9); } DvbDescriptor descriptors() const { return DvbDescriptor(getData() + 12, descriptorsLength); } DvbPmtSectionEntry entries() const { return DvbPmtSectionEntry(getData() + 12 + descriptorsLength, getLength() - (16 + descriptorsLength)); } private: Q_DISABLE_COPY(DvbPmtSection) void initPmtSection(const char *data, int size); int descriptorsLength; }; class DvbSdtSection : public DvbStandardSection { public: DvbSdtSection(const char *data, int size) { initSdtSection(data, size); } explicit DvbSdtSection(const QByteArray &byteArray) { initSdtSection(byteArray.constData(), byteArray.size()); } ~DvbSdtSection() { } int originalNetworkId() const { return (at(8) << 8) | at(9); } DvbSdtSectionEntry entries() const { return DvbSdtSectionEntry(getData() + 11, getLength() - 15); } private: Q_DISABLE_COPY(DvbSdtSection) void initSdtSection(const char *data, int size); }; class DvbEitSection : public DvbStandardSection { public: DvbEitSection(const char *data, int size) { initEitSection(data, size); } explicit DvbEitSection(const QByteArray &byteArray) { initEitSection(byteArray.constData(), byteArray.size()); } ~DvbEitSection() { } int serviceId() const { return (at(3) << 8) | at(4); } int transportStreamId() const { return (at(8) << 8) | at(9); } int originalNetworkId() const { return (at(10) << 8) | at(11); } DvbEitSectionEntry entries() const { return DvbEitSectionEntry(getData() + 14, getLength() - 18); } private: Q_DISABLE_COPY(DvbEitSection) void initEitSection(const char *data, int size); }; class DvbNitSection : public DvbStandardSection { public: DvbNitSection(const char *data, int size) { initNitSection(data, size); } explicit DvbNitSection(const QByteArray &byteArray) { initNitSection(byteArray.constData(), byteArray.size()); } ~DvbNitSection() { } DvbDescriptor descriptors() const { return DvbDescriptor(getData() + 10, descriptorsLength); } DvbNitSectionEntry entries() const { return DvbNitSectionEntry(getData() + 12 + descriptorsLength, entriesLength); } private: Q_DISABLE_COPY(DvbNitSection) void initNitSection(const char *data, int size); int descriptorsLength; int entriesLength; }; class AtscMgtSection : public DvbStandardSection { public: AtscMgtSection(const char *data, int size) { initMgtSection(data, size); } explicit AtscMgtSection(const QByteArray &byteArray) { initMgtSection(byteArray.constData(), byteArray.size()); } ~AtscMgtSection() { } int entryCount() const { return (at(9) << 8) | at(10); } AtscMgtSectionEntry entries() const { return AtscMgtSectionEntry(getData() + 11, getLength() - 17); } private: Q_DISABLE_COPY(AtscMgtSection) void initMgtSection(const char *data, int size); }; class AtscVctSection : public DvbStandardSection { public: AtscVctSection(const char *data, int size) { initVctSection(data, size); } explicit AtscVctSection(const QByteArray &byteArray) { initVctSection(byteArray.constData(), byteArray.size()); } ~AtscVctSection() { } int entryCount() const { return at(9); } AtscVctSectionEntry entries() const { return AtscVctSectionEntry(getData() + 10, getLength() - 14); } private: Q_DISABLE_COPY(AtscVctSection) void initVctSection(const char *data, int size); }; class AtscEitSection : public DvbStandardSection { public: AtscEitSection(const char *data, int size) { initEitSection(data, size); } explicit AtscEitSection(const QByteArray &byteArray) { initEitSection(byteArray.constData(), byteArray.size()); } ~AtscEitSection() { } int sourceId() const { return (at(3) << 8) | at(4); } int entryCount() const { return at(9); } AtscEitSectionEntry entries() const { return AtscEitSectionEntry(getData() + 10, getLength() - 14); } private: Q_DISABLE_COPY(AtscEitSection) void initEitSection(const char *data, int size); }; class AtscEttSection : public DvbStandardSection { public: AtscEttSection(const char *data, int size) { initEttSection(data, size); } explicit AtscEttSection(const QByteArray &byteArray) { initEttSection(byteArray.constData(), byteArray.size()); } ~AtscEttSection() { } int sourceId() const { return (at(9) << 8) | at(10); } int eventId() const { return (at(11) << 6) | (at(12) >> 2); } int messageType() const { return (at(12) & 0x3); } QString text() const { return AtscPsipText::convertText(getData() + 13, getLength() - 17); } private: Q_DISABLE_COPY(AtscEttSection) void initEttSection(const char *data, int size); }; #endif /* DVBSI_H */ diff --git a/src/dvb/dvbtab.cpp b/src/dvb/dvbtab.cpp index 7cc3441..9f4fec1 100644 --- a/src/dvb/dvbtab.cpp +++ b/src/dvb/dvbtab.cpp @@ -1,609 +1,609 @@ /* * dvbtab.cpp * * Copyright (C) 2007-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "../log.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../osdwidget.h" #include "dvbchanneldialog.h" #include "dvbconfigdialog.h" #include "dvbepg.h" #include "dvbepgdialog.h" #include "dvbliveview.h" #include "dvbmanager.h" #include "dvbrecordingdialog.h" #include "dvbscandialog.h" #include "dvbtab.h" class DvbTimeShiftCleaner : public QThread { public: explicit DvbTimeShiftCleaner(QObject *parent) : QThread(parent) { } ~DvbTimeShiftCleaner() { wait(); } void remove(const QString &path_, const QStringList &files_); private: - void run(); + void run() override; QString path; QStringList files; }; void DvbTimeShiftCleaner::remove(const QString &path_, const QStringList &files_) { path = path_; files = files_; start(); } void DvbTimeShiftCleaner::run() { // delete files asynchronously because it may block for several seconds foreach (const QString &file, files) { QFile::remove(path + QLatin1Char('/') + file); } } DvbTab::DvbTab(QMenu *menu, KActionCollection *collection, MediaWidget *mediaWidget_) : mediaWidget(mediaWidget_) { manager = new DvbManager(mediaWidget, this); mediaRecordIcon = QIcon::fromTheme(QLatin1String("media-record"), QIcon(":media-record")); documentSaveIcon = QIcon::fromTheme(QLatin1String("document-save"), QIcon(":document-save")); QAction *channelsAction = new QAction(QIcon::fromTheme(QLatin1String("video-television"), QIcon(":video-television")), i18n("Channels"), this); channelsAction->setShortcut(Qt::Key_C); connect(channelsAction, SIGNAL(triggered(bool)), this, SLOT(showChannelDialog())); menu->addAction(collection->addAction(QLatin1String("dvb_channels"), channelsAction)); QAction *epgAction = new QAction(QIcon::fromTheme(QLatin1String("view-list-details"), QIcon(":view-list-details")), i18n("Program Guide"), this); epgAction->setShortcut(Qt::Key_G); connect(epgAction, SIGNAL(triggered(bool)), this, SLOT(toggleEpgDialog())); menu->addAction(collection->addAction(QLatin1String("dvb_epg"), epgAction)); QAction *osdAction = new QAction(QIcon::fromTheme(QLatin1String("dialog-information"), QIcon(":dialog-information")), i18n("OSD"), this); osdAction->setShortcut(Qt::Key_O); connect(osdAction, SIGNAL(triggered(bool)), manager->getLiveView(), SLOT(toggleOsd())); menu->addAction(collection->addAction(QLatin1String("dvb_osd"), osdAction)); QAction *recordingsAction = new QAction(QIcon::fromTheme(QLatin1String("view-pim-calendar"), QIcon(":view-pim-calendar")), i18nc("dialog", "Recording Schedule"), this); recordingsAction->setShortcut(Qt::Key_R); connect(recordingsAction, SIGNAL(triggered(bool)), this, SLOT(showRecordingDialog())); menu->addAction(collection->addAction(QLatin1String("dvb_recordings"), recordingsAction)); menu->addSeparator(); instantRecordAction = new QAction(documentSaveIcon, i18n("Instant Record"), this); instantRecordAction->setCheckable(true); connect(instantRecordAction, SIGNAL(triggered(bool)), this, SLOT(instantRecord(bool))); menu->addAction(collection->addAction(QLatin1String("dvb_instant_record"), instantRecordAction)); menu->addSeparator(); QAction *configureAction = new QAction(QIcon::fromTheme(QLatin1String("configure"), QIcon(":configure")), i18nc("@action:inmenu", "Configure Television..."), this); connect(configureAction, SIGNAL(triggered()), this, SLOT(configureDvb())); menu->addAction(collection->addAction(QLatin1String("settings_dvb"), configureAction)); connect(manager->getLiveView(), SIGNAL(previous()), this, SLOT(previousChannel())); connect(manager->getLiveView(), SIGNAL(next()), this, SLOT(nextChannel())); connect(manager->getRecordingModel(), SIGNAL(recordingRemoved(DvbSharedRecording)), this, SLOT(recordingRemoved(DvbSharedRecording))); QBoxLayout *boxLayout = new QHBoxLayout(this); boxLayout->setMargin(0); splitter = new QSplitter(this); boxLayout->addWidget(splitter); leftWidget = new QWidget(splitter); QBoxLayout *leftLayout = new QVBoxLayout(leftWidget); boxLayout = new QHBoxLayout(); boxLayout->addWidget(new QLabel(i18n("Search:"))); QLineEdit *lineEdit = new QLineEdit(leftWidget); lineEdit->setClearButtonEnabled(true); boxLayout->addWidget(lineEdit); leftLayout->addLayout(boxLayout); channelView = new DvbChannelView(leftWidget); channelView->setContextMenuPolicy(Qt::ActionsContextMenu); channelProxyModel = new DvbChannelTableModel(this); channelView->setModel(channelProxyModel); channelView->setRootIsDecorated(false); if (!channelView->header()->restoreState(QByteArray::fromBase64( KSharedConfig::openConfig()->group("DVB").readEntry("ChannelViewState", QByteArray())))) { channelView->sortByColumn(0, Qt::AscendingOrder); } channelView->setSortingEnabled(true); channelView->addEditAction(); connect(channelView, SIGNAL(activated(QModelIndex)), this, SLOT(playChannel(QModelIndex))); connect(channelView, SIGNAL(channelPidsUpdated(DvbSharedChannel)), this, SLOT(channelPidsUpdated(DvbSharedChannel))); channelProxyModel->setChannelModel(manager->getChannelModel()); connect(lineEdit, SIGNAL(textChanged(QString)), channelProxyModel, SLOT(setFilter(QString))); manager->setChannelView(channelView); leftLayout->addWidget(channelView); boxLayout = new QHBoxLayout(); const QSize BUTTON_SIZE = QSize(22, 22); QToolButton *toolButton = new QToolButton(leftWidget); toolButton->setDefaultAction(configureAction); toolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); toolButton->setFixedSize(BUTTON_SIZE); boxLayout->addWidget(toolButton); toolButton = new QToolButton(leftWidget); toolButton->setDefaultAction(channelsAction); toolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); toolButton->setFixedSize(BUTTON_SIZE); boxLayout->addWidget(toolButton); toolButton = new QToolButton(leftWidget); toolButton->setDefaultAction(epgAction); toolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); toolButton->setFixedSize(BUTTON_SIZE); boxLayout->addWidget(toolButton); toolButton = new QToolButton(leftWidget); toolButton->setDefaultAction(recordingsAction); toolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); toolButton->setFixedSize(BUTTON_SIZE); boxLayout->addWidget(toolButton); toolButton = new QToolButton(leftWidget); toolButton->setDefaultAction(instantRecordAction); toolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); toolButton->setFixedSize(BUTTON_SIZE); boxLayout->addWidget(toolButton); leftLayout->addLayout(boxLayout); QWidget *mediaContainer = new QWidget(splitter); mediaLayout = new QHBoxLayout(mediaContainer); mediaLayout->setMargin(0); splitter->setStretchFactor(1, 1); connect(mediaWidget, SIGNAL(osdKeyPressed(int)), this, SLOT(osdKeyPressed(int))); connect(&osdChannelTimer, SIGNAL(timeout()), this, SLOT(tuneOsdChannel())); lastChannel = KSharedConfig::openConfig()->group("DVB").readEntry("LastChannel"); splitter->restoreState(QByteArray::fromBase64( KSharedConfig::openConfig()->group("DVB").readEntry("TabSplitterState", QByteArray()))); timeShiftCleaner = new DvbTimeShiftCleaner(this); QTimer *timer = new QTimer(this); timer->start(30000); connect(timer, SIGNAL(timeout()), this, SLOT(cleanTimeShiftFiles())); autoHideMenu = false; } DvbTab::~DvbTab() { KSharedConfig::openConfig()->group("DVB").writeEntry("TabSplitterState", splitter->saveState().toBase64()); KSharedConfig::openConfig()->group("DVB").writeEntry("ChannelViewState", channelView->header()->saveState().toBase64()); if (!currentChannel.isEmpty()) { lastChannel = currentChannel; } KSharedConfig::openConfig()->group("DVB").writeEntry("LastChannel", lastChannel); } void DvbTab::playChannel(const QString &nameOrNumber) { DvbChannelModel *channelModel = manager->getChannelModel(); DvbSharedChannel channel; int number = nameOrNumber.toInt(); if (number > 0) { channel = channelModel->findChannelByNumber(number); } if (!channel.isValid()) { channel = channelModel->findChannelByName(nameOrNumber); } if (channel.isValid()) { playChannel(channel, channelProxyModel->find(channel)); } } void DvbTab::playLastChannel() { if (!manager->getLiveView()->getChannel().isValid() && !currentChannel.isEmpty()) { lastChannel = currentChannel; } DvbSharedChannel channel = manager->getChannelModel()->findChannelByName(lastChannel); if (channel.isValid()) { playChannel(channel, channelProxyModel->find(channel)); } } void DvbTab::channelPidsUpdated(const DvbSharedChannel &updatedChannel) { DvbSharedChannel channel = manager->getChannelModel()->findChannelByName(lastChannel); MediaWidget::PlaybackStatus status; /* * This slot is called when a channel is played and * a channel configuration that would require to reload * the Digital TV filtering was changed at the editor, * e. g. if a PID has changed. * The common reason is if someone wants to change the * audio PID, in order to select a different language or * enable/disable narration. */ // Do nothing if the edited channel is not the active one if (updatedChannel != channel) return; status = mediaWidget->getPlaybackStatus(); // Ignore channels that are not being played if (status == MediaWidget::Idle) return; // Re-run the channel play, in order to update the // PID filters playLastChannel(); #if 0 /* * TODO: ideally, if the channel is paused, editing it should * preserve it. However, togglePause() will only work after * the channel starts to play, with takes some time. So, * the code below won't work. * Also, now the old pause status data is not valid anymore, * as it contains a different PID, so we need to flush the * data anyway - or implement a way more complex logic that * would be handling a paused off-line file, and a new * on-line paused file. * * So, for now, let's just assume that changing the PIDs * while pausing a video would flush the old pause stuff * and un-pause the channel. */ if (status == MediaWidget::Paused) { qInfo() << "Toggling pause"; mediaWidget->togglePause(); } #endif } void DvbTab::toggleOsd() { manager->getLiveView()->toggleOsd(); } void DvbTab::toggleInstantRecord() { instantRecordAction->trigger(); } void DvbTab::enableDvbDump() { manager->enableDvbDump(); } void DvbTab::mouse_move(int x, int) { if (!autoHideMenu) return; unsetCursor(); leftWidget->setVisible(x >= 0 && x < 120); } void DvbTab::toggleDisplayMode(MediaWidget::DisplayMode displayMode) { switch (displayMode) { case MediaWidget::FullScreenMode: case MediaWidget::FullScreenReturnToMinimalMode: case MediaWidget::MinimalMode: leftWidget->hide(); autoHideMenu = true; break; case MediaWidget::NormalMode: leftWidget->show(); autoHideMenu = false; break; } } void DvbTab::osdKeyPressed(int key) { if ((key >= Qt::Key_0) && (key <= Qt::Key_9)) { osdChannel += QString::number(key - Qt::Key_0); osdChannelTimer.start(1500); mediaWidget->getOsdWidget()->showText(i18nc("osd", "Channel: %1_", osdChannel), 1500); } } void DvbTab::mayCloseApplication(bool *ok, QWidget *parent) { if (*ok) { DvbRecordingModel *recordingModel = manager->getRecordingModel(); if (recordingModel->hasActiveRecordings()) { if (KMessageBox::warningYesNo(parent, i18nc("message box", "Kaffeine is currently recording programs.\n" "Do you really want to close the application?")) != KMessageBox::Yes) { *ok = false; } return; } if (recordingModel->hasRecordings()) { if (KMessageBox::questionYesNo(parent, i18nc("message box", "Kaffeine has scheduled recordings.\n" "Do you really want to close the application?"), QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(), QLatin1String("ScheduledRecordings")) != KMessageBox::Yes) { *ok = false; } return; } } } void DvbTab::showChannelDialog() { QDialog *dialog = new DvbScanDialog(manager, this); dialog->setAttribute(Qt::WA_DeleteOnClose, true); dialog->setModal(true); dialog->show(); } void DvbTab::showRecordingDialog() { DvbRecordingDialog::showDialog(manager, this); } void DvbTab::toggleEpgDialog() { if (epgDialog.isNull()) { epgDialog = new DvbEpgDialog(manager, this); epgDialog->setAttribute(Qt::WA_DeleteOnClose, true); epgDialog->setCurrentChannel(manager->getLiveView()->getChannel()); epgDialog->setModal(false); epgDialog->show(); } else { epgDialog->deleteLater(); epgDialog = NULL; } } void DvbTab::instantRecord(bool checked) { if (checked) { const DvbSharedChannel &channel = manager->getLiveView()->getChannel(); if (!channel.isValid()) { instantRecordAction->setChecked(false); return; } DvbRecording recording; QList epgEntries = manager->getEpgModel()->getCurrentNext(channel); if (!epgEntries.isEmpty()) { recording.name = epgEntries.at(0)->title(); } if (recording.name.isEmpty()) { recording.name = (channel->name + QTime::currentTime().toString(QLatin1String("-hhmmss"))); } recording.channel = channel; recording.begin = QDateTime::currentDateTime().toUTC(); recording.duration = QTime(12, 0); instantRecording = manager->getRecordingModel()->addRecording(recording); instantRecordings.push_back(instantRecording); instantRecordAction->setIcon(mediaRecordIcon); mediaWidget->getOsdWidget()->showText(i18nc("osd", "Instant Record Started"), 1500); } else { manager->getRecordingModel()->removeRecording(instantRecording); instantRecordings.removeOne(instantRecording); instantRecordAction->setIcon(documentSaveIcon); mediaWidget->getOsdWidget()->showText(i18nc("osd", "Instant Record Stopped"), 1500); } } void DvbTab::recordingRemoved(const DvbSharedRecording &recording) { if (instantRecording == recording) { instantRecording = DvbSharedRecording(); instantRecordAction->setChecked(false); instantRecordAction->setIcon(documentSaveIcon); mediaWidget->getOsdWidget()->showText(i18nc("osd", "Instant Record Stopped"), 1500); } instantRecordings.removeOne(recording); } void DvbTab::configureDvb() { QDialog *dialog = new DvbConfigDialog(manager, this); dialog->setAttribute(Qt::WA_DeleteOnClose, true); dialog->setModal(true); dialog->show(); } void DvbTab::tuneOsdChannel() { int number = osdChannel.toInt(); osdChannel.clear(); osdChannelTimer.stop(); DvbSharedChannel channel = manager->getChannelModel()->findChannelByNumber(number); if (channel.isValid()) { playChannel(channel, channelProxyModel->find(channel)); } } void DvbTab::playChannel(const QModelIndex &index) { if (index.isValid()) { playChannel(channelProxyModel->value(index), index); } } void DvbTab::previousChannel() { QModelIndex index = channelView->currentIndex(); if (index.isValid()) { playChannel(index.sibling(index.row() - 1, index.column())); } } void DvbTab::nextChannel() { QModelIndex index = channelView->currentIndex(); if (index.isValid()) { playChannel(index.sibling(index.row() + 1, index.column())); } } void DvbTab::cleanTimeShiftFiles() { if (timeShiftCleaner->isRunning()) { return; } QDir dir(manager->getTimeShiftFolder()); QStringList entries = dir.entryList(QStringList(QLatin1String("TimeShift-*.m2t")), QDir::Files, QDir::Name); if (entries.count() < 2) { return; } entries.removeLast(); timeShiftCleaner->remove(dir.path(), entries); } void DvbTab::activate() { mediaLayout->addWidget(mediaWidget); mediaWidget->setFocus(); } void DvbTab::playChannel(const DvbSharedChannel &channel, const QModelIndex &index) { QIcon *icon; DvbSharedRecording *recording; bool isRecording; if (!channel.isValid()) { qCWarning(logDvb, "Channel is invalid"); return; } if (!currentChannel.isEmpty()) { lastChannel = currentChannel; } recording = getInstantRecording(channel); if (recording) { instantRecording = *recording; isRecording = true; icon = &mediaRecordIcon; } else { isRecording = false; icon = &documentSaveIcon; } instantRecordAction->setChecked(isRecording); instantRecordAction->setIcon(*icon); channelView->setCurrentIndex(index); currentChannel = channel->name; manager->getLiveView()->playChannel(channel); if (!epgDialog.isNull()) { epgDialog->setCurrentChannel(manager->getLiveView()->getChannel()); } } DvbSharedRecording *DvbTab::getInstantRecording(DvbSharedChannel ch) { DvbSharedRecording *ret = NULL; QListIterator i(instantRecordings); while (i.hasNext()) { DvbSharedRecording r = i.next(); if(r.constData()->channel == ch) { ret = &r; break; } } return ret; } diff --git a/src/dvb/dvbtab.h b/src/dvb/dvbtab.h index 48276e4..41a08cc 100644 --- a/src/dvb/dvbtab.h +++ b/src/dvb/dvbtab.h @@ -1,123 +1,123 @@ /* * dvbtab.h * * Copyright (C) 2007-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DVBTAB_H #define DVBTAB_H #include #include #include #include #include #include "../tabbase.h" #include "../mediawidget.h" #include "dvbrecording.h" class QModelIndex; class QSplitter; class QAction; class KActionCollection; class QMenu; class DvbChannelTableModel; class DvbChannelView; class DvbEpgDialog; class DvbTimeShiftCleaner; class MediaWidget; class DvbTab : public TabBase { Q_OBJECT public: DvbTab(QMenu *menu, KActionCollection *collection, MediaWidget *mediaWidget_); ~DvbTab(); void playChannel(const QString &nameOrNumber); void playLastChannel(); void toggleOsd(); void toggleInstantRecord(); - void toggleDisplayMode(MediaWidget::DisplayMode displayMode); - void mouse_move(int x, int y); + void toggleDisplayMode(MediaWidget::DisplayMode displayMode) override; + void mouse_move(int x, int y) override; DvbManager *getManager() const { return manager; } void enableDvbDump(); public slots: void osdKeyPressed(int key); void mayCloseApplication(bool *ok, QWidget *parent); private slots: void showChannelDialog(); void toggleEpgDialog(); void showRecordingDialog(); void instantRecord(bool checked); void recordingRemoved(const DvbSharedRecording &recording); void configureDvb(); void tuneOsdChannel(); void playChannel(const QModelIndex &index); void previousChannel(); void nextChannel(); void cleanTimeShiftFiles(); void channelPidsUpdated(const DvbSharedChannel &updatedChannel); private: - void activate(); + void activate() override; void playChannel(const DvbSharedChannel &channel, const QModelIndex &index); DvbSharedRecording *getInstantRecording(DvbSharedChannel ch); MediaWidget *mediaWidget; DvbManager *manager; QAction *instantRecordAction; QList instantRecordings; DvbSharedRecording instantRecording; QSplitter *splitter; QWidget *leftWidget; DvbChannelTableModel *channelProxyModel; DvbChannelView *channelView; QPointer epgDialog; QLayout *mediaLayout; QString osdChannel; QTimer osdChannelTimer; QString currentChannel; QString lastChannel; QIcon mediaRecordIcon; QIcon documentSaveIcon; bool autoHideMenu; DvbTimeShiftCleaner *timeShiftCleaner; }; #ifndef HAVE_DVB #error HAVE_DVB must be defined #endif /* HAVE_DVB */ #if HAVE_DVB == 0 inline void DvbTab::playChannel(QString const &) { } inline void DvbTab::playLastChannel() { } inline void DvbTab::toggleOsd() { } inline void DvbTab::toggleInstantRecord() { } inline void DvbTab::osdKeyPressed(int) { } #endif /* HAVE_DVB == 0 */ #endif /* DVBTAB_H */ diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 066f6f6..ad2d5cb 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,911 +1,911 @@ /* * mainwindow.cpp * * Copyright (C) 2007-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "log.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "configuration.h" #include "configurationdialog.h" #include "dbusobjects.h" #include "dvb/dvbtab.h" #include "mainwindow.h" #include "playlist/playlisttab.h" // log categories. Should match log.h Q_LOGGING_CATEGORY(logCam, "kaffeine.cam") Q_LOGGING_CATEGORY(logDev, "kaffeine.dev") Q_LOGGING_CATEGORY(logDvb, "kaffeine.dvb") Q_LOGGING_CATEGORY(logDvbSi, "kaffeine.dvbsi") Q_LOGGING_CATEGORY(logEpg, "kaffeine.epg") Q_LOGGING_CATEGORY(logConfig, "kaffeine.config") Q_LOGGING_CATEGORY(logMediaWidget, "kaffeine.mediawidget") Q_LOGGING_CATEGORY(logPlaylist, "kaffeine.playlist") Q_LOGGING_CATEGORY(logSql, "kaffeine.sql") Q_LOGGING_CATEGORY(logVlc, "kaffeine.vlc") #define FILTER_RULE "kaffeine.*.debug=true" #define CATEGORIES "cam, dev, dvb, dvbsi, epg, config, mediawidget, playlist, sql, vlc" class StackedLayout : public QStackedLayout { public: explicit StackedLayout(QWidget *parent) : QStackedLayout(parent) { } ~StackedLayout() { } - QSize minimumSize() const + QSize minimumSize() const override { QWidget *widget = currentWidget(); if (widget != NULL) { return widget->minimumSizeHint(); } return QSize(); } }; class StartTab : public TabBase { public: explicit StartTab(MainWindow *mainWindow); ~StartTab() { } private: - void activate() { } + void activate() override { } QAbstractButton *addShortcut(const QString &name, const QIcon &icon, QWidget *parent); }; StartTab::StartTab(MainWindow *mainWindow) { setBackgroundRole(QPalette::Base); setAutoFillBackground(true); QGridLayout *gridLayout = new QGridLayout(this); gridLayout->setAlignment(Qt::AlignCenter); gridLayout->setMargin(10); gridLayout->setSpacing(15); QAbstractButton *button = addShortcut(i18n("&1 Play File"), QIcon::fromTheme(QLatin1String("video-x-generic"), QIcon(":video-x-generic")), this); button->setShortcut(Qt::Key_1); button->setWhatsThis(i18n("Open dialog to play a file")); connect(button, SIGNAL(clicked()), mainWindow, SLOT(open())); gridLayout->addWidget(button, 0, 0); button = addShortcut(i18n("&2 Play Audio CD"), QIcon::fromTheme(QLatin1String("media-optical-audio"), QIcon(":media-optical-audio")), this); button->setShortcut(Qt::Key_2); button->setWhatsThis(i18n("Start playing an audio CD. It assumes that the CD is already there at the CD drive")); connect(button, SIGNAL(clicked()), mainWindow, SLOT(openAudioCd())); gridLayout->addWidget(button, 0, 1); button = addShortcut(i18n("&3 Play Video CD"), QIcon::fromTheme(QLatin1String("media-optical"), QIcon(":media-optical-video")), this); button->setShortcut(Qt::Key_3); button->setWhatsThis(i18n("Start playing a Video CD. It assumes that the CD is already there at the CD drive")); connect(button, SIGNAL(clicked()), mainWindow, SLOT(openVideoCd())); gridLayout->addWidget(button, 0, 2); button = addShortcut(i18n("&4 Play DVD"), QIcon::fromTheme(QLatin1String("media-optical"), QIcon(":media-optical")), this); button->setShortcut(Qt::Key_4); button->setWhatsThis(i18n("Start playing a DVD. It assumes that the DVD is already there at the DVD drive")); connect(button, SIGNAL(clicked()), mainWindow, SLOT(openDvd())); gridLayout->addWidget(button, 1, 0); #if HAVE_DVB == 1 button = addShortcut(i18n("&5 Digital TV"), QIcon::fromTheme(QLatin1String("video-television"), QIcon(":video-television")), this); button->setShortcut(Qt::Key_5); button->setWhatsThis("Open the Digital TV live view window. If the TV channels are already scanned, it will start playing the last channel"); connect(button, SIGNAL(clicked()), mainWindow, SLOT(playDvb())); gridLayout->addWidget(button, 1, 1); #endif /* HAVE_DVB == 1 */ } QAbstractButton *StartTab::addShortcut(const QString &name, const QIcon &icon, QWidget *parent) { // QPushButton has visual problems with big icons QToolButton *button = new QToolButton(parent); button->setText(name); button->setIcon(icon); button->setFocusPolicy(Qt::NoFocus); button->setIconSize(QSize(48, 48)); button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); return button; } class PlayerTab : public TabBase { public: explicit PlayerTab(MediaWidget *mediaWidget_); ~PlayerTab() { } - void activate() + void activate() override { layout()->addWidget(mediaWidget); mediaWidget->setFocus(); } private: MediaWidget *mediaWidget; }; PlayerTab::PlayerTab(MediaWidget *mediaWidget_) : mediaWidget(mediaWidget_) { QHBoxLayout *layout = new QHBoxLayout(this); layout->setMargin(0); } void MainWindow::run() { // Allow the user to enable or disable debugging // We handle this before the other parameters, as it may affect some // early debug messages // // --debug enables all debugging categories. It is possible to enable // each category individually by calling Kaffeine with: // QT_LOGGING_RULES="epg.debug=true" kaffeine if (parser->isSet("debug")) { QLoggingCategory::defaultCategory()->setEnabled(QtDebugMsg, true); QLoggingCategory::setFilterRules(QStringLiteral(FILTER_RULE)); } else { QLoggingCategory::setFilterRules(QStringLiteral("kaffeine.*.debug=false")); } readSettings(); setAttribute(Qt::WA_DeleteOnClose, true); // menu structure QMenuBar *menuBar = QMainWindow::menuBar(); collection = new KActionCollection(this); QMenu *menu = new QMenu(i18n("&File"), this); menuBar->addMenu(menu); QAction *action = KStandardAction::open(this, SLOT(open()), collection); menu->addAction(collection->addAction(QLatin1String("file_open"), action)); action = new QAction(QIcon::fromTheme(QLatin1String("text-html"), QIcon(":text-html")), i18nc("@action:inmenu", "Open URL..."), collection); action->setShortcut(Qt::CTRL | Qt::Key_U); connect(action, SIGNAL(triggered(bool)), this, SLOT(openUrl())); menu->addAction(collection->addAction(QLatin1String("file_open_url"), action)); actionOpenRecent = KStandardAction::openRecent(this, SLOT(openUrl(QUrl)), collection); actionOpenRecent->loadEntries(KSharedConfig::openConfig()->group("Recent Files")); menu->addAction(collection->addAction(QLatin1String("file_open_recent"), actionOpenRecent)); menu->addSeparator(); action = new QAction(QIcon::fromTheme(QLatin1String("media-optical-audio"), QIcon(":media-optical-audio")), i18n("Play Audio CD"), collection); connect(action, SIGNAL(triggered(bool)), this, SLOT(openAudioCd())); menu->addAction(collection->addAction(QLatin1String("file_play_audiocd"), action)); action = new QAction(QIcon::fromTheme(QLatin1String("media-optical"), QIcon(":media-optical-video")), i18n("Play Video CD"), collection); connect(action, SIGNAL(triggered(bool)), this, SLOT(openVideoCd())); menu->addAction(collection->addAction(QLatin1String("file_play_videocd"), action)); action = new QAction(QIcon::fromTheme(QLatin1String("media-optical"), QIcon(":media-optical")), i18n("Play DVD"), collection); connect(action, SIGNAL(triggered(bool)), this, SLOT(openDvd())); menu->addAction(collection->addAction(QLatin1String("file_play_dvd"), action)); action = new QAction(QIcon::fromTheme(QLatin1String("media-optical"), QIcon(":media-optical")), i18nc("@action:inmenu", "Play DVD Folder"), collection); connect(action, SIGNAL(triggered()), this, SLOT(playDvdFolder())); menu->addAction(collection->addAction(QLatin1String("file_play_dvd_folder"), action)); menu->addSeparator(); action = KStandardAction::quit(this, SLOT(close()), collection); menu->addAction(collection->addAction(QLatin1String("file_quit"), action)); QMenu *playerMenu = new QMenu(i18n("&Playback"), this); menuBar->addMenu(playerMenu); QMenu *playlistMenu = new QMenu(i18nc("menu bar", "Play&list"), this); menuBar->addMenu(playlistMenu); #if HAVE_DVB == 1 QMenu *dvbMenu = new QMenu(i18n("&Television"), this); menuBar->addMenu(dvbMenu); #endif /* HAVE_DVB == 1 */ menu = new QMenu(i18n("&Settings"), this); menuBar->addMenu(menu); action = KStandardAction::keyBindings(this, SLOT(configureKeys()), collection); menu->addAction(collection->addAction(QLatin1String("settings_keys"), action)); action = KStandardAction::preferences(this, SLOT(configureKaffeine()), collection); menu->addAction(collection->addAction(QLatin1String("settings_kaffeine"), action)); menuBar->addSeparator(); KHelpMenu *helpMenu = new KHelpMenu(this, *aboutData); menuBar->addMenu(helpMenu->menu()); // navigation bar - keep in sync with TabIndex enum! navigationBar = new QToolBar(QLatin1String("navigation_bar")); this->addToolBar(Qt::LeftToolBarArea, navigationBar); connect(navigationBar, SIGNAL(orientationChanged(Qt::Orientation)), this, SLOT(navigationBarOrientationChanged(Qt::Orientation))); tabBar = new QTabBar(navigationBar); tabBar->addTab(QIcon::fromTheme(QLatin1String("start-here-kde"), QIcon(":start-here-kde")), i18n("Start")); tabBar->addTab(QIcon::fromTheme(QLatin1String("kaffeine"), QIcon(":kaffeine")), i18n("Playback")); tabBar->addTab(QIcon::fromTheme(QLatin1String("view-media-playlist"), QIcon(":view-media-playlist")), i18n("Playlist")); #if HAVE_DVB == 1 tabBar->addTab(QIcon::fromTheme(QLatin1String("video-television"), QIcon(":video-television")), i18n("Television")); #endif /* HAVE_DVB == 1 */ tabBar->setShape(QTabBar::RoundedWest); tabBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); connect(tabBar, SIGNAL(currentChanged(int)), this, SLOT(activateTab(int))); navigationBar->addWidget(tabBar); // control bar controlBar = new QToolBar(QLatin1String("control_bar"), this); this->addToolBar(Qt::BottomToolBarArea, controlBar); controlBar->setToolButtonStyle(Qt::ToolButtonIconOnly); autoHideControlBar = false; cursorHideTimer = new QTimer(this); cursorHideTimer->setInterval(1500); cursorHideTimer->setSingleShot(true); connect(cursorHideTimer, SIGNAL(timeout()), this, SLOT(hideCursor())); // main area QWidget *widget = new QWidget(this); stackedLayout = new StackedLayout(widget); setCentralWidget(widget); mediaWidget = new MediaWidget(playerMenu, controlBar, collection, widget); connect(mediaWidget, SIGNAL(displayModeChanged()), this, SLOT(displayModeChanged())); connect(mediaWidget, SIGNAL(changeCaption(QString)), this, SLOT(setWindowTitle(QString))); // tabs - keep in sync with TabIndex enum! TabBase *startTab = new StartTab(this); tabs.append(startTab); stackedLayout->addWidget(startTab); playerTab = new PlayerTab(mediaWidget); playerTab->activate(); tabs.append(playerTab); stackedLayout->addWidget(playerTab); playlistTab = new PlaylistTab(playlistMenu, collection, mediaWidget); tabs.append(playlistTab); stackedLayout->addWidget(playlistTab); #if HAVE_DVB == 1 dvbTab = new DvbTab(dvbMenu, collection, mediaWidget); connect(this, SIGNAL(mayCloseApplication(bool*,QWidget*)), dvbTab, SLOT(mayCloseApplication(bool*,QWidget*))); tabs.append(dvbTab); stackedLayout->addWidget(dvbTab); #endif /* HAVE_DVB == 1 */ currentTabIndex = StartTabId; // actions also have to work if the menu bar is hidden (fullscreen) collection->addAssociatedWidget(this); // restore custom key bindings collection->readSettings(); // Tray menu menu = new QMenu(i18n("Kaffeine"), this); action = new QAction(i18n("Play &File"), this); connect(action, SIGNAL(triggered(bool)), this, SLOT(open())); menu->addAction(action); action = new QAction(i18n("Play &Audio CD"), this); connect(action, SIGNAL(triggered(bool)), this, SLOT(openAudioCd())); menu->addAction(action); action = new QAction(i18n("Play &Video CD"), this); connect(action, SIGNAL(triggered(bool)), this, SLOT(openVideoCd())); menu->addAction(action); action = new QAction(i18n("Play &DVD"), this); connect(action, SIGNAL(triggered(bool)), this, SLOT(openDvd())); menu->addAction(action); #if HAVE_DVB == 1 action = new QAction(i18n("&Watch Digital TV"), this); connect(action, SIGNAL(triggered(bool)), this, SLOT(playDvb())); menu->addAction(action); #endif action = new QAction(i18n("&Quit"), this); connect(action, SIGNAL(triggered(bool)), this, SLOT(close())); menu->addAction(action); // Tray Icon and its menu QMenu *trayMenu = new QMenu(this); trayIcon = new QSystemTrayIcon(this); trayIcon->setContextMenu(trayMenu); trayIcon->setIcon(QIcon::fromTheme(QLatin1String("kaffeine"), QIcon(":kaffeine"))); trayIcon->setToolTip(i18n("Kaffeine")); trayIcon->setContextMenu(menu); connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayShowHide(QSystemTrayIcon::ActivationReason)) ); // make sure that the bars are visible (fullscreen -> quit -> restore -> hidden) menuBar->show(); navigationBar->show(); controlBar->show(); trayIcon->show(); // workaround setAutoSaveSettings() which doesn't accept "IconOnly" as initial state controlBar->setToolButtonStyle(Qt::ToolButtonIconOnly); // initialize random number generator qsrand(QTime(0, 0, 0).msecsTo(QTime::currentTime())); // initialize dbus objects QDBusConnection::sessionBus().registerObject(QLatin1String("/"), new MprisRootObject(this), QDBusConnection::ExportAllContents); QDBusConnection::sessionBus().registerObject(QLatin1String("/Player"), new MprisPlayerObject(this, mediaWidget, playlistTab, this), QDBusConnection::ExportAllContents); QDBusConnection::sessionBus().registerObject(QLatin1String("/TrackList"), new MprisTrackListObject(playlistTab, this), QDBusConnection::ExportAllContents); #if HAVE_DVB == 1 QDBusConnection::sessionBus().registerObject(QLatin1String("/Television"), new DBusTelevisionObject(dvbTab, this), QDBusConnection::ExportAllContents); #endif /* HAVE_DVB == 1 */ QDBusConnection::sessionBus().registerService(QLatin1String("org.mpris.kaffeine")); parseArgs(); show(); } MainWindow::~MainWindow() { actionOpenRecent->saveEntries(KSharedConfig::openConfig()->group("Recent Files")); if (!temporaryUrls.isEmpty()) { KIO::del(temporaryUrls); } int value = 0; switch (mediaWidget->getDisplayMode()) { case MediaWidget::NormalMode: value = 0; break; case MediaWidget::MinimalMode: value = 1; break; case MediaWidget::FullScreenMode: value = 2; break; case MediaWidget::FullScreenReturnToMinimalMode: value = 2; break; } KSharedConfig::openConfig()->group("MainWindow").writeEntry("DisplayMode", value); } void MainWindow::close() { bool ok = true; #if HAVE_DVB == 1 dvbTab->mayCloseApplication(&ok, mediaWidget); #endif /* HAVE_DVB == 1 */ if (ok) { writeSettings(); mediaWidget->stop(); QMainWindow::close(); Configuration::detach(); QCoreApplication::exit(0); } } void MainWindow::readSettings() { QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName()); const QByteArray geometry = settings.value("geometry", QByteArray()).toByteArray(); if (geometry.isEmpty()) { const QRect availableGeometry = QApplication::desktop()->availableGeometry(this); resize(availableGeometry.width() / 3, availableGeometry.height() / 2); move((availableGeometry.width() - width()) / 2, (availableGeometry.height() - height()) / 2); } else { restoreGeometry(geometry); } } void MainWindow::writeSettings() { QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName()); settings.setValue("geometry", saveGeometry()); } MainWindow::MainWindow(KAboutData *aboutData, QCommandLineParser *parser) { this->aboutData = aboutData; this->parser = parser; parser->addOption(QCommandLineOption(QStringList() << QLatin1String("d") << QLatin1String("debug"), i18n("Enable all debug messages. Please notice that Kaffeine also allows enabling debug messages per category, by using the environment var:\nQT_LOGGING_RULES=kaffeine.category.debug=true\nwhere 'category' can be:\n" CATEGORIES))); parser->addOption(QCommandLineOption(QStringList() << QLatin1String("tempfile"), i18n("The files/URLs opened by the application will be deleted after use"))); parser->addOption(QCommandLineOption(QStringList() << QLatin1String("f") << QLatin1String("fullscreen"), i18n("Start in full screen mode"))); parser->addOption(QCommandLineOption(QStringList() << QLatin1String("m") << QLatin1String("minimal"), i18n("Start in minimal mode"))); parser->addOption(QCommandLineOption(QStringList() << QLatin1String("t") << QLatin1String("alwaysontop"), i18n("Start with always on top"))); parser->addOption(QCommandLineOption(QStringList() << QLatin1String("audiocd"), i18n("Play Audio CD"))); parser->addOption(QCommandLineOption(QStringList() << QLatin1String("videocd"), i18n("Play Video CD"))); parser->addOption(QCommandLineOption(QStringList() << QLatin1String("dvd"), i18n("Play DVD"))); parser->addOption(QCommandLineOption(QStringList() << QLatin1String("aspectratio"), "Force starting with an specific aspect ratio", QLatin1String("aspect ratio"))); #if HAVE_DVB == 1 parser->addOption(QCommandLineOption(QStringList() << QLatin1String("dumpdvb"), i18nc("command line option", "Dump dvb data (debug option)"))); parser->addOption(QCommandLineOption(QStringList() << QLatin1String("channel"), i18nc("command line option", "Play TV channel"), QLatin1String("name / number"))); parser->addOption(QCommandLineOption(QStringList() << QLatin1String("tv"), i18nc("command line option", "(deprecated option)"), QLatin1String("channel"))); parser->addOption(QCommandLineOption(QStringList() << QLatin1String("lastchannel"), i18nc("command line option", "Play last tuned TV channel"))); #endif parser->addPositionalArgument(QLatin1String("[file]"), i18n("Files or URLs to play")); } void MainWindow::parseArgs(const QString workingDirectory) { /* Parse first arguments that aren't mutually exclusive */ if (parser->isSet("alwaysontop")) { Qt::WindowFlags flags = this->windowFlags(); flags |= Qt::WindowStaysOnTopHint; this->setWindowFlags(flags); } if (parser->isSet("fullscreen")) { mediaWidget->setDisplayMode(MediaWidget::FullScreenMode); } else if (parser->isSet("minimal")) { mediaWidget->setDisplayMode(MediaWidget::MinimalMode); } else { // set display mode switch (Configuration::instance()->getStartupDisplayMode()) { case Configuration::StartupNormalMode: // nothing to do break; case Configuration::StartupMinimalMode: mediaWidget->setDisplayMode(MediaWidget::MinimalMode); break; case Configuration::StartupFullScreenMode: mediaWidget->setDisplayMode(MediaWidget::FullScreenMode); break; case Configuration::StartupRememberLastSetting: { int value = KSharedConfig::openConfig()->group("MainWindow").readEntry("DisplayMode", 0); switch (value) { case 0: // nothing to do break; case 1: mediaWidget->setDisplayMode(MediaWidget::MinimalMode); break; case 2: mediaWidget->setDisplayMode(MediaWidget::FullScreenMode); break; } break; } /* case */ } /* switch */ } if (parser->isSet("aspectratio")) { MediaWidget::AspectRatio aspectRatio = MediaWidget::AspectRatioAuto; QString aspect; aspect = parser->value("aspectratio"); if (aspect == "1:1") aspectRatio = MediaWidget::AspectRatio1_1; else if (aspect == "4:3") aspectRatio = MediaWidget::AspectRatio4_3; else if (aspect == "5:4") aspectRatio = MediaWidget::AspectRatio5_4; else if (aspect == "16:9") aspectRatio = MediaWidget::AspectRatio16_9; else if (aspect == "16:10") aspectRatio = MediaWidget::AspectRatio16_10; else if (aspect == "2.21:1") aspectRatio = MediaWidget::AspectRatio221_100; else if (aspect == "2.35:1") aspectRatio = MediaWidget::AspectRatio235_100; else if (aspect == "2.39:1") aspectRatio = MediaWidget::AspectRatio239_100; mediaWidget->setAspectRatio(aspectRatio); } #if HAVE_DVB == 1 if (parser->isSet("dumpdvb")) { dvbTab->enableDvbDump(); } #endif /* * Now, parse arguments that are mutually exclusive */ #if HAVE_DVB == 1 QString channel; channel = parser->value("channel"); if (!channel.isEmpty()) { activateTab(DvbTabId); dvbTab->playChannel(channel); return; } channel = parser->value("tv"); if (!channel.isEmpty()) { activateTab(DvbTabId); dvbTab->playChannel(channel); return; } if (parser->isSet("lastchannel")) { activateTab(DvbTabId); dvbTab->playLastChannel(); return; } #endif /* HAVE_DVB == 1 */ if (parser->isSet("audiocd")) { if (parser->positionalArguments().count() > 0) { openAudioCd(parser->positionalArguments().first()); } else { openAudioCd(); } return; } if (parser->isSet("videocd")) { if (parser->positionalArguments().count() > 0) { openVideoCd(parser->positionalArguments().first()); } else { openVideoCd(); } return; } if (parser->isSet("dvd")) { if (parser->positionalArguments().count() > 0) { openDvd(parser->positionalArguments().first()); } else { openDvd(); } return; } if (parser->positionalArguments().count() > 0) { QList urls; for (int i = 0; i < parser->positionalArguments().count(); ++i) { QUrl url = QUrl::fromUserInput(parser->positionalArguments().at(i), workingDirectory); if (url.isValid()) { urls.append(url); } } if (parser->isSet("tempfile")) { temporaryUrls.append(urls); } if (urls.size() >= 2) { activateTab(PlaylistTabId); playlistTab->appendToVisiblePlaylist(urls, true); } else if (!urls.isEmpty()) { openUrl(urls.at(0)); } return; } } void MainWindow::displayModeChanged() { MediaWidget::DisplayMode displayMode = mediaWidget->getDisplayMode(); switch (displayMode) { case MediaWidget::FullScreenMode: case MediaWidget::FullScreenReturnToMinimalMode: setWindowState(windowState() | Qt::WindowFullScreen); break; case MediaWidget::MinimalMode: case MediaWidget::NormalMode: setWindowState(windowState() & (~Qt::WindowFullScreen)); break; } switch (displayMode) { case MediaWidget::FullScreenMode: case MediaWidget::FullScreenReturnToMinimalMode: case MediaWidget::MinimalMode: menuBar()->hide(); navigationBar->hide(); controlBar->hide(); autoHideControlBar = true; cursorHideTimer->start(); break; case MediaWidget::NormalMode: menuBar()->show(); navigationBar->show(); controlBar->show(); autoHideControlBar = false; cursorHideTimer->stop(); unsetCursor(); break; } tabs.at(currentTabIndex)->toggleDisplayMode(displayMode); } void MainWindow::trayShowHide(QSystemTrayIcon::ActivationReason reason) { if (reason != QSystemTrayIcon::DoubleClick) return; if (isVisible()) hide(); else { show(); raise(); setFocus(); } } void MainWindow::open() { if (isMinimized()) showNormal(); QList urls = QFileDialog::getOpenFileUrls(this, i18nc("@title:window", "Open files"), QUrl(), MediaWidget::extensionFilter()); // trayIcon->showMessage("Open", "Opening file(s)"); if (urls.size() >= 2) { activateTab(PlaylistTabId); playlistTab->appendToVisiblePlaylist(urls, true); } else if (!urls.isEmpty()) { openUrl(urls.at(0)); } } void MainWindow::openUrl() { openUrl(QInputDialog::getText(this, i18nc("@title:window", "Open URL"), i18n("Enter a URL:"))); } void MainWindow::openUrl(const QUrl &url) { if (!url.isValid()) { return; } // we need to copy "url" because addUrl() may invalidate it QUrl copy(url); actionOpenRecent->addUrl(copy); // moves the url to the top of the list if (currentTabIndex != PlaylistTabId) { activateTab(PlayerTabId); } playlistTab->appendToVisiblePlaylist(QList() << copy, true); } void MainWindow::openAudioCd(const QString &device) { if (isMinimized()) showNormal(); activateTab(PlayerTabId); mediaWidget->playAudioCd(device); } void MainWindow::openVideoCd(const QString &device) { if (isMinimized()) showNormal(); activateTab(PlayerTabId); mediaWidget->playVideoCd(device); } void MainWindow::openDvd(const QString &device) { if (isMinimized()) showNormal(); activateTab(PlayerTabId); mediaWidget->playDvd(device); } void MainWindow::playDvdFolder() { QString folder = QFileDialog::getExistingDirectory(this, QString()); if (!folder.isEmpty()) { openDvd(folder); } } void MainWindow::playDvb() { if (isMinimized()) showNormal(); activateTab(DvbTabId); dvbTab->playLastChannel(); } void MainWindow::configureKeys() { KShortcutsDialog::configure(collection); } void MainWindow::configureKaffeine() { QDialog *dialog = new ConfigurationDialog(this); dialog->setAttribute(Qt::WA_DeleteOnClose, true); dialog->setModal(true); dialog->show(); } void MainWindow::navigationBarOrientationChanged(Qt::Orientation orientation) { if (orientation == Qt::Horizontal) { tabBar->setShape(QTabBar::RoundedNorth); } else { tabBar->setShape(QTabBar::RoundedWest); } } void MainWindow::activateTab(int tabIndex) { currentTabIndex = tabIndex; tabBar->setCurrentIndex(tabIndex); /* * Ensure that menus and timers will be properly shown, * according with the selected display mode. */ stackedLayout->setCurrentIndex(currentTabIndex); tabs.at(currentTabIndex)->activate(); emit displayModeChanged(); } void MainWindow::hideCursor() { setCursor(Qt::BlankCursor); } void MainWindow::closeEvent(QCloseEvent *event) { bool ok = true; emit mayCloseApplication(&ok, this); if (!ok) { event->ignore(); } else { MainWindow::close(); } } bool MainWindow::event(QEvent *event) { bool retVal = QMainWindow::event(event); // this has to be done before calling setVisible() // FIXME we depend on QEvent::HoverMove (instead of QEvent::MouseMove) // but the latter depends on mouse tracking being enabled on this widget // and all its children (especially the video widget) ... switch (event->type()) { case QEvent::HoverMove: { int x = reinterpret_cast (event)->pos().x(); int y = reinterpret_cast (event)->pos().y(); if ((y < 0) || (y >= height()) || (x < 0) || (x >= width())) { // QHoverEvent sometimes reports quite strange coordinates - ignore them return retVal; } if (autoHideControlBar) { cursorHideTimer->stop(); unsetCursor(); switch (toolBarArea(controlBar)) { case Qt::TopToolBarArea: controlBar->setVisible(y < 60); break; case Qt::BottomToolBarArea: controlBar->setVisible(y >= (height() - 60)); menuBar()->setVisible(y < 60); break; default: break; } if (controlBar->isHidden() || menuBar()->isHidden()) { cursorHideTimer->start(); } } tabs.at(currentTabIndex)->mouse_move(x, y); break; } default: break; } return retVal; } void MainWindow::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Escape) { mediaWidget->setDisplayMode(MediaWidget::NormalMode); } QMainWindow::keyPressEvent(event); } void MainWindow::leaveEvent(QEvent *event) { if (autoHideControlBar) { menuBar()->setVisible(false); controlBar->setVisible(false); tabs.at(currentTabIndex)->mouse_move(-1, -1); } QMainWindow::leaveEvent(event); } diff --git a/src/mainwindow.h b/src/mainwindow.h index db05e32..9b6c771 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -1,109 +1,109 @@ /* * mainwindow.h * * Copyright (C) 2007-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include #include #include "mediawidget.h" class KAboutData; class QStackedLayout; class KCmdLineOptions; class KRecentFilesAction; class QTabBar; class DvbTab; class PlayerTab; class PlaylistTab; class TabBase; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(KAboutData *aboutData, QCommandLineParser *parser); ~MainWindow(); void run(); void parseArgs(const QString workingDirectory = ""); signals: void mayCloseApplication(bool *ok, QWidget *parent); private slots: void displayModeChanged(); void open(); void close(); void openUrl(); void openUrl(const QUrl &url); void openAudioCd(const QString &device = QString()); void openVideoCd(const QString &device = QString()); void openDvd(const QString &device = QString()); void playDvdFolder(); void playDvb(); void configureKeys(); void configureKaffeine(); void navigationBarOrientationChanged(Qt::Orientation orientation); void activateTab(int tabIndex); void hideCursor(); void trayShowHide(QSystemTrayIcon::ActivationReason reason); private: enum TabIndex { StartTabId = 0, PlayerTabId = 1, PlaylistTabId = 2, DvbTabId = 3 }; void readSettings(); void writeSettings(); - void closeEvent(QCloseEvent *event); - bool event(QEvent *event); - void keyPressEvent(QKeyEvent *event); - void leaveEvent(QEvent *event); + void closeEvent(QCloseEvent *event) override; + bool event(QEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void leaveEvent(QEvent *event) override; KAboutData *aboutData; QCommandLineParser *parser; QSystemTrayIcon *trayIcon; KActionCollection *collection; KRecentFilesAction *actionOpenRecent; QToolBar *navigationBar; QTabBar *tabBar; QToolBar *controlBar; bool autoHideControlBar; QTimer *cursorHideTimer; QList temporaryUrls; MediaWidget *mediaWidget; QStackedLayout *stackedLayout; QList tabs; int currentTabIndex; PlayerTab *playerTab; PlaylistTab *playlistTab; DvbTab *dvbTab; }; #endif /* MAINWINDOW_H */ diff --git a/src/mediawidget_p.h b/src/mediawidget_p.h index 611f63f..4bc9bdf 100644 --- a/src/mediawidget_p.h +++ b/src/mediawidget_p.h @@ -1,104 +1,104 @@ /* * mediawidget_p.h * * Copyright (C) 2007-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef MEDIAWIDGET_P_H #define MEDIAWIDGET_P_H #include #include #include "mediawidget.h" class QTimeEdit; class JumpToPositionDialog : public QDialog { public: explicit JumpToPositionDialog(MediaWidget *mediaWidget_); ~JumpToPositionDialog(); private: - void accept(); + void accept() override; MediaWidget *mediaWidget; QTimeEdit *timeEdit; }; class SeekSlider : public QSlider { public: explicit SeekSlider(QWidget *parent) : QSlider(parent) { } ~SeekSlider() { } private: - void mousePressEvent(QMouseEvent *event); - void wheelEvent(QWheelEvent *event); + void mousePressEvent(QMouseEvent *event) override; + void wheelEvent(QWheelEvent *event) override; }; class MediaSourceUrl : public MediaSource { public: MediaSourceUrl(const QUrl &url_, const QUrl &subtitleUrl_) : url(url_), subtitleUrl(subtitleUrl_) { } ~MediaSourceUrl() { } - Type getType() const { return Url; } - QUrl getUrl() const { return url; } + Type getType() const override { return Url; } + QUrl getUrl() const override { return url; } QUrl url; QUrl subtitleUrl; }; class MediaSourceAudioCd : public MediaSource { public: explicit MediaSourceAudioCd(const QUrl &url_) : url(url_) { } ~MediaSourceAudioCd() { } - Type getType() const { return AudioCd; } - QUrl getUrl() const { return url; } + Type getType() const override { return AudioCd; } + QUrl getUrl() const override { return url; } QUrl url; }; class MediaSourceVideoCd : public MediaSource { public: explicit MediaSourceVideoCd(const QUrl &url_) : url(url_) { } ~MediaSourceVideoCd() { } - Type getType() const { return VideoCd; } - QUrl getUrl() const { return url; } + Type getType() const override { return VideoCd; } + QUrl getUrl() const override { return url; } QUrl url; }; class MediaSourceDvd : public MediaSource { public: explicit MediaSourceDvd(const QUrl &url_) : url(url_) { } ~MediaSourceDvd() { } - Type getType() const { return Dvd; } - QUrl getUrl() const { return url; } + Type getType() const override { return Dvd; } + QUrl getUrl() const override { return url; } QUrl url; }; #endif /* MEDIAWIDGET_P_H */ diff --git a/src/osdwidget.h b/src/osdwidget.h index 757b2fb..ccadef2 100644 --- a/src/osdwidget.h +++ b/src/osdwidget.h @@ -1,61 +1,61 @@ /* * osdwidget.h * * Copyright (C) 2009-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef OSDWIDGET_H #define OSDWIDGET_H #include class OsdObject { public: OsdObject() { } virtual ~OsdObject() { } virtual QPixmap paintOsd(QRect &rect, const QFont &font, Qt::LayoutDirection direction) = 0; }; class OsdWidget : public QWidget { Q_OBJECT public: explicit OsdWidget(QWidget *parent); ~OsdWidget(); void showText(const QString &text, int duration); // duration: msecs void showObject(OsdObject *osdObject_, int duration); // duration: msecs void hideObject(); protected: - void paintEvent(QPaintEvent *); + void paintEvent(QPaintEvent *) override; private slots: void hideOsd(); private: QRect rect; QPixmap pixmap; OsdObject *osdObject; QTimer *timer; }; #endif /* OSDWIDGET_H */ diff --git a/src/playlist/playlistmodel.h b/src/playlist/playlistmodel.h index 69de38f..39ea29b 100644 --- a/src/playlist/playlistmodel.h +++ b/src/playlist/playlistmodel.h @@ -1,132 +1,132 @@ /* * playlistmodel.h * * Copyright (C) 2009-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef PLAYLISTMODEL_H #define PLAYLISTMODEL_H #include #include #include "../mediawidget.h" class PlaylistTrack { public: PlaylistTrack() : trackNumber(-1), currentSubtitle(-1) { } ~PlaylistTrack() { } QUrl url; QString title; QString artist; QString album; int trackNumber; QTime length; QList subtitles; int currentSubtitle; }; class Playlist { public: Playlist() : currentTrack(-1) { } ~Playlist() { } const PlaylistTrack &at(int index) const { return tracks.at(index); } enum Format { Invalid, Kaffeine, // read-only M3U, PLS, XSPF }; bool load(const QUrl &url_, Format format); bool save(Format format) const; QUrl url; QString title; QList tracks; int currentTrack; private: void appendTrack(PlaylistTrack &track); QUrl fromFileOrUrl(const QString &fileOrUrl) const; QUrl fromRelativeUrl(const QString &trackUrlString) const; QString toFileOrUrl(const QUrl &trackUrl) const; QString toRelativeUrl(const QUrl &trackUrl) const; bool loadKaffeinePlaylist(QIODevice *device); bool loadM3UPlaylist(QIODevice *device); void saveM3UPlaylist(QIODevice *device) const; bool loadPLSPlaylist(QIODevice *device); void savePLSPlaylist(QIODevice *device) const; bool loadXSPFPlaylist(QIODevice *device); void saveXSPFPlaylist(QIODevice *device) const; }; class PlaylistModel : public QAbstractTableModel { Q_OBJECT public: PlaylistModel(Playlist *visiblePlaylist_, QObject *parent); ~PlaylistModel(); void setVisiblePlaylist(Playlist *visiblePlaylist_); Playlist *getVisiblePlaylist() const; void appendUrls(Playlist *playlist, const QList &urls, bool playImmediately); void removeRows(Playlist *playlist, int row, int count); void setCurrentTrack(Playlist *playlist, int track); void updateTrackLength(Playlist *playlist, int length); void updateTrackMetadata(Playlist *playlist, const QMap &metadata); public slots: void clearVisiblePlaylist(); signals: void appendPlaylist(Playlist *playlist, bool playImmediately); void playTrack(Playlist *playlist, int track); private: void insertUrls(Playlist *playlist, int row, const QList &urls, bool playImmediately); - int columnCount(const QModelIndex &parent) const; - int rowCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - bool removeRows(int row, int count, const QModelIndex &parent); - void sort(int column, Qt::SortOrder order); - - Qt::ItemFlags flags(const QModelIndex &index) const; - QStringList mimeTypes() const; - Qt::DropActions supportedDropActions() const; - QMimeData *mimeData(const QModelIndexList &indexes) const; + int columnCount(const QModelIndex &parent) const override; + int rowCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + bool removeRows(int row, int count, const QModelIndex &parent) override; + void sort(int column, Qt::SortOrder order) override; + + Qt::ItemFlags flags(const QModelIndex &index) const override; + QStringList mimeTypes() const override; + Qt::DropActions supportedDropActions() const override; + QMimeData *mimeData(const QModelIndexList &indexes) const override; bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, - const QModelIndex &parent); + const QModelIndex &parent) override; Playlist *visiblePlaylist; }; #endif /* PLAYLISTMODEL_H */ diff --git a/src/playlist/playlisttab.cpp b/src/playlist/playlisttab.cpp index 01f1290..3f5b833 100644 --- a/src/playlist/playlisttab.cpp +++ b/src/playlist/playlisttab.cpp @@ -1,874 +1,874 @@ /* * playlisttab.cpp * * Copyright (C) 2009-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "../log.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "playlistmodel.h" #include "playlisttab.h" PlaylistBrowserModel::PlaylistBrowserModel(PlaylistModel *playlistModel_, Playlist *temporaryPlaylist, QObject *parent) : QAbstractListModel(parent), playlistModel(playlistModel_), currentPlaylist(-1) { playlists.append(temporaryPlaylist); QFile file(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1String("/playlistsK4")); if (!file.open(QIODevice::ReadOnly)) { file.setFileName(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1String("/playlists")); if (!file.open(QIODevice::ReadOnly)) { qCWarning(logPlaylist, "Cannot open file %s", qPrintable(file.fileName())); return; } } QDataStream stream(&file); stream.setVersion(QDataStream::Qt_4_4); unsigned int version; stream >> version; bool hasMetadata = true; bool hasSubtitles = true; if (version == 0xc39637a1) { // compatibility code hasMetadata = false; hasSubtitles = false; } else if (version == 0x2e00f3ea) { // compatibility code hasSubtitles = false; } else if (version != 0x361c4a3c) { qCWarning(logPlaylist, "Cannot read file %s", qPrintable(file.fileName())); return; } while (!stream.atEnd()) { Playlist *playlist = new Playlist(); stream >> playlist->title; QString urlString; stream >> urlString; playlist->url = urlString; int count; stream >> count; for (int i = 0; (i < count) && !stream.atEnd(); ++i) { PlaylistTrack track; stream >> urlString; track.url = urlString; if (hasMetadata) { stream >> track.title; stream >> track.artist; stream >> track.album; stream >> track.trackNumber; stream >> track.length; } else { track.title = track.url.fileName(); } if (hasSubtitles) { QStringList subtitleStrings; stream >> subtitleStrings; foreach (const QString &subtitleString, subtitleStrings) { track.subtitles.append(subtitleString); } stream >> track.currentSubtitle; } playlist->tracks.append(track); } if (stream.status() != QDataStream::Ok) { qCWarning(logPlaylist, "Cannot read file %s", qPrintable(file.fileName())); delete playlist; break; } playlists.append(playlist); } } PlaylistBrowserModel::~PlaylistBrowserModel() { QFile file(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1String("/playlistsK4")); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { qCWarning(logPlaylist, "Cannot open file %s", qPrintable(file.fileName())); return; } QDataStream stream(&file); stream.setVersion(QDataStream::Qt_4_4); int version = 0x361c4a3c; stream << version; for (int i = 1; i < playlists.size(); ++i) { const Playlist *playlist = playlists.at(i); stream << playlist->title; stream << playlist->url.url(); stream << playlist->tracks.size(); foreach (const PlaylistTrack &track, playlist->tracks) { stream << track.url.url(); stream << track.title; stream << track.artist; stream << track.album; stream << track.trackNumber; stream << track.length; QStringList subtitleStrings; for (int j = 0; j < track.subtitles.size(); ++j) { const QUrl &url = track.subtitles.at(j); subtitleStrings.append(url.url()); } stream << subtitleStrings; stream << track.currentSubtitle; } } qDeleteAll(playlists); } int PlaylistBrowserModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return playlists.size(); } QVariant PlaylistBrowserModel::data(const QModelIndex &index, int role) const { if (role == Qt::DecorationRole) { if (index.row() == currentPlaylist) { return QIcon::fromTheme(QLatin1String("arrow-right"), QIcon(":arrow-right")); } } else if (role == Qt::DisplayRole) { return playlists.at(index.row())->title; } return QVariant(); } bool PlaylistBrowserModel::removeRows(int row, int count, const QModelIndex &parent) { if (parent.isValid()) { return false; } if (row == 0) { ++row; if ((--count) == 0) { return false; } } beginRemoveRows(QModelIndex(), row, row + count - 1); Playlist *visiblePlaylist = playlistModel->getVisiblePlaylist(); for (int i = row; i < (row + count); ++i) { if (playlists.at(i) == visiblePlaylist) { if ((row + count) < playlists.size()) { playlistModel->setVisiblePlaylist(playlists.at(row + count)); } else { playlistModel->setVisiblePlaylist(playlists.at(row - 1)); } } } QList::Iterator begin = playlists.begin() + row; QList::Iterator end = begin + count; qDeleteAll(begin, end); playlists.erase(begin, end); if (currentPlaylist >= row) { if (currentPlaylist >= (row + count)) { currentPlaylist -= count; } else { currentPlaylist = -1; emit playTrack(NULL, -1); } } endRemoveRows(); return true; } Qt::ItemFlags PlaylistBrowserModel::flags(const QModelIndex &index) const { return QAbstractListModel::flags(index) | Qt::ItemIsEditable; } bool PlaylistBrowserModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (role == Qt::EditRole) { QString title = value.toString(); if (title.isEmpty()) { return false; } playlists.at(index.row())->title = title; emit dataChanged(index, index); return true; } return false; } void PlaylistBrowserModel::append(Playlist *playlist) { beginInsertRows(QModelIndex(), playlists.size(), playlists.size()); playlists.append(playlist); endInsertRows(); } Playlist *PlaylistBrowserModel::getPlaylist(int row) const { return playlists.at(row); } void PlaylistBrowserModel::setCurrentPlaylist(Playlist *playlist) { int oldPlaylist = currentPlaylist; currentPlaylist = playlists.indexOf(playlist); if (oldPlaylist == currentPlaylist) { return; } if (oldPlaylist != -1) { playlistModel->setCurrentTrack(playlists.at(oldPlaylist), -1); QModelIndex modelIndex = index(oldPlaylist, 0); emit dataChanged(modelIndex, modelIndex); } if (currentPlaylist != -1) { QModelIndex modelIndex = index(currentPlaylist, 0); emit dataChanged(modelIndex, modelIndex); } } Playlist *PlaylistBrowserModel::getCurrentPlaylist() const { if (currentPlaylist >= 0) { return playlists.at(currentPlaylist); } else { return playlistModel->getVisiblePlaylist(); } } class PlaylistBrowserView : public QListView { public: explicit PlaylistBrowserView(QWidget *parent) : QListView(parent) { } ~PlaylistBrowserView() { } protected: - void keyPressEvent(QKeyEvent *event); + void keyPressEvent(QKeyEvent *event) override; }; void PlaylistBrowserView::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Delete) { QModelIndexList selectedRows = selectionModel()->selectedRows(); qSort(selectedRows); for (int i = selectedRows.size() - 1; i >= 0; --i) { // FIXME compress model()->removeRows(selectedRows.at(i).row(), 1); } return; } QListView::keyPressEvent(event); } PlaylistView::PlaylistView(QWidget *parent) : QTreeView(parent) { } PlaylistView::~PlaylistView() { } void PlaylistView::removeSelectedRows() { QModelIndexList selectedRows = selectionModel()->selectedRows(); qSort(selectedRows); for (int i = selectedRows.size() - 1; i >= 0; --i) { // FIXME compress model()->removeRows(selectedRows.at(i).row(), 1); } } void PlaylistView::contextMenuEvent(QContextMenuEvent *event) { if (!currentIndex().isValid()) { return; } QMenu::exec(actions(), event->globalPos()); } void PlaylistView::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Delete) { removeSelectedRows(); return; } QTreeView::keyPressEvent(event); } PlaylistTab::PlaylistTab(QMenu *menu, KActionCollection *collection, MediaWidget *mediaWidget_) : mediaWidget(mediaWidget_) { Playlist *temporaryPlaylist = new Playlist(); temporaryPlaylist->title = i18nc("playlist browser", "Temporary Playlist"); playlistModel = new PlaylistModel(temporaryPlaylist, this); connect(playlistModel, SIGNAL(playTrack(Playlist*,int)), this, SLOT(playTrack(Playlist*,int))); connect(playlistModel, SIGNAL(appendPlaylist(Playlist*,bool)), this, SLOT(appendPlaylist(Playlist*,bool))); playlistBrowserModel = new PlaylistBrowserModel(playlistModel, temporaryPlaylist, this); playlistModel->setVisiblePlaylist(playlistBrowserModel->getPlaylist(0)); connect(playlistBrowserModel, SIGNAL(playTrack(Playlist*,int)), this, SLOT(playTrack(Playlist*,int))); connect(mediaWidget, SIGNAL(playlistPrevious()), this, SLOT(playPreviousTrack())); connect(mediaWidget, SIGNAL(playlistNext()), this, SLOT(playNextTrack())); connect(mediaWidget, SIGNAL(playlistUrlsDropped(QList)), this, SLOT(appendUrls(QList))); connect(mediaWidget, SIGNAL(playlistTrackLengthChanged(int)), this, SLOT(updateTrackLength(int))); connect(mediaWidget, SIGNAL(playlistTrackMetadataChanged(QMap)), this, SLOT(updateTrackMetadata(QMap))); repeatAction = new QAction(QIcon::fromTheme(QLatin1String("media-playlist-repeat"), QIcon(":media-playlist-repeat")), i18nc("playlist menu", "Repeat"), this); repeatAction->setCheckable(true); menu->addAction(collection->addAction(QLatin1String("playlist_repeat"), repeatAction)); randomAction = new QAction(QIcon::fromTheme(QLatin1String("media-playlist-shuffle"), QIcon(":media-playlist-shuffle")), i18nc("playlist menu", "Random"), this); randomAction->setCheckable(true); menu->addAction(collection->addAction(QLatin1String("playlist_random"), randomAction)); QAction *addSubtitleAction = new QAction(QIcon::fromTheme(QLatin1String("application-x-subrip"), QIcon(":application-x-subrip")), i18n("Add Subtitle"), this); collection->addAction(QLatin1String("playlist_add_subtitle"), addSubtitleAction); QAction *removeTrackAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete"), QIcon(":edit-delete")), i18nc("@action", "Remove"), this); collection->addAction(QLatin1String("playlist_remove_track"), removeTrackAction); QAction *clearAction = new QAction(QIcon::fromTheme(QLatin1String("edit-clear-list"), QIcon(":edit-clear-list")), i18nc("remove all items from a list", "Clear"), this); connect(clearAction, SIGNAL(triggered(bool)), playlistModel, SLOT(clearVisiblePlaylist())); menu->addAction(collection->addAction(QLatin1String("playlist_clear"), clearAction)); menu->addSeparator(); QAction *newAction = new QAction(QIcon::fromTheme(QLatin1String("list-add"), QIcon(":list-add")), i18nc("@action:inmenu playlist", "New"), this); connect(newAction, SIGNAL(triggered(bool)), this, SLOT(newPlaylist())); menu->addAction(collection->addAction(QLatin1String("playlist_new"), newAction)); QAction *renameAction = new QAction(QIcon::fromTheme(QLatin1String("edit-rename"), QIcon(":edit-rename")), i18nc("rename an entry in a list", "Rename"), this); connect(renameAction, SIGNAL(triggered(bool)), this, SLOT(renamePlaylist())); menu->addAction(collection->addAction(QLatin1String("playlist_rename"), renameAction)); QAction *removePlaylistAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete"), QIcon(":edit-delete")), i18nc("@action", "Remove"), this); connect(removePlaylistAction, SIGNAL(triggered(bool)), this, SLOT(removePlaylist())); menu->addAction(collection->addAction(QLatin1String("playlist_remove"), removePlaylistAction)); QAction *savePlaylistAction = KStandardAction::save(this, SLOT(savePlaylist()), this); menu->addAction(collection->addAction(QLatin1String("playlist_save"), savePlaylistAction)); QAction *savePlaylistAsAction = KStandardAction::saveAs(this, SLOT(savePlaylistAs()), this); menu->addAction(collection->addAction(QLatin1String("playlist_save_as"), savePlaylistAsAction)); QBoxLayout *widgetLayout = new QHBoxLayout(this); widgetLayout->setMargin(0); QSplitter *horizontalSplitter = new QSplitter(this); widgetLayout->addWidget(horizontalSplitter); QSplitter *verticalSplitter = new QSplitter(Qt::Vertical, horizontalSplitter); QWidget *widget = new QWidget(verticalSplitter); QBoxLayout *sideLayout = new QVBoxLayout(widget); sideLayout->setMargin(0); QBoxLayout *boxLayout = new QHBoxLayout(); QToolButton *toolButton = new QToolButton(widget); toolButton->setDefaultAction(newAction); toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); boxLayout->addWidget(toolButton); toolButton = new QToolButton(widget); toolButton->setDefaultAction(renameAction); toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); boxLayout->addWidget(toolButton); toolButton = new QToolButton(widget); toolButton->setDefaultAction(removePlaylistAction); toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); boxLayout->addWidget(toolButton); toolButton = new QToolButton(widget); toolButton->setDefaultAction(savePlaylistAction); toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); boxLayout->addWidget(toolButton); toolButton = new QToolButton(widget); toolButton->setDefaultAction(savePlaylistAsAction); toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); boxLayout->addWidget(toolButton); boxLayout->addStretch(); sideLayout->addLayout(boxLayout); playlistBrowserView = new PlaylistBrowserView(widget); playlistBrowserView->addAction(newAction); playlistBrowserView->addAction(renameAction); playlistBrowserView->addAction(removePlaylistAction); playlistBrowserView->addAction(savePlaylistAction); playlistBrowserView->addAction(savePlaylistAsAction); playlistBrowserView->setContextMenuPolicy(Qt::ActionsContextMenu); playlistBrowserView->setModel(playlistBrowserModel); connect(playlistBrowserView, SIGNAL(activated(QModelIndex)), this, SLOT(playlistActivated(QModelIndex))); sideLayout->addWidget(playlistBrowserView); // KFileWidget creates a local event loop which can cause bad side // effects (because the main window isn't fully constructed yet) fileWidgetSplitter = verticalSplitter; QTimer::singleShot(0, this, SLOT(createFileWidget())); verticalSplitter = new QSplitter(Qt::Vertical, horizontalSplitter); widget = new QWidget(verticalSplitter); sideLayout = new QVBoxLayout(widget); sideLayout->setMargin(0); boxLayout = new QHBoxLayout(); toolButton = new QToolButton(widget); toolButton->setDefaultAction(repeatAction); toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); boxLayout->addWidget(toolButton); toolButton = new QToolButton(widget); toolButton->setDefaultAction(randomAction); toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); boxLayout->addWidget(toolButton); toolButton = new QToolButton(widget); toolButton->setDefaultAction(removeTrackAction); toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); boxLayout->addWidget(toolButton); toolButton = new QToolButton(widget); toolButton->setDefaultAction(clearAction); toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); boxLayout->addWidget(toolButton); boxLayout->addStretch(); sideLayout->addLayout(boxLayout); playlistView = new PlaylistView(widget); playlistView->setAlternatingRowColors(true); playlistView->setDragDropMode(QAbstractItemView::DragDrop); playlistView->setRootIsDecorated(false); playlistView->setSelectionMode(QAbstractItemView::ExtendedSelection); playlistView->setModel(playlistModel); playlistView->sortByColumn(-1, Qt::AscendingOrder); playlistView->setSortingEnabled(true); playlistView->addAction(addSubtitleAction); connect(addSubtitleAction, SIGNAL(triggered(bool)), this, SLOT(addSubtitle())); playlistView->addAction(removeTrackAction); connect(removeTrackAction, SIGNAL(triggered(bool)), playlistView, SLOT(removeSelectedRows())); connect(playlistView, SIGNAL(activated(QModelIndex)), this, SLOT(playTrack(QModelIndex))); sideLayout->addWidget(playlistView); QWidget *mediaContainer = new QWidget(verticalSplitter); mediaLayout = new QHBoxLayout(mediaContainer); mediaLayout->setMargin(0); verticalSplitter->setStretchFactor(1, 1); horizontalSplitter->setStretchFactor(1, 1); } PlaylistTab::~PlaylistTab() { } void PlaylistTab::appendToCurrentPlaylist(const QList &urls, bool playImmediately) { playlistModel->appendUrls(playlistBrowserModel->getCurrentPlaylist(), urls, playImmediately); } void PlaylistTab::appendToVisiblePlaylist(const QList &urls, bool playImmediately) { playlistModel->appendUrls(playlistModel->getVisiblePlaylist(), urls, playImmediately); } void PlaylistTab::removeTrack(int row) { Playlist *currentPlaylist = playlistBrowserModel->getCurrentPlaylist(); if ((row >= 0) && (row < currentPlaylist->tracks.size())) { playlistModel->removeRows(currentPlaylist, row, 1); } } void PlaylistTab::setRandom(bool random) { randomAction->setChecked(random); } void PlaylistTab::setRepeat(bool repeat) { repeatAction->setChecked(repeat); } int PlaylistTab::getCurrentTrack() const { const Playlist *currentPlaylist = playlistBrowserModel->getCurrentPlaylist(); if (currentPlaylist->currentTrack >= 0) { return currentPlaylist->currentTrack; } else { return 0; } } int PlaylistTab::getTrackCount() const { return playlistBrowserModel->getCurrentPlaylist()->tracks.size(); } bool PlaylistTab::getRandom() const { return randomAction->isChecked(); } bool PlaylistTab::getRepeat() const { return repeatAction->isChecked(); } void PlaylistTab::createFileWidget() { KFileWidget *fileWidget = new KFileWidget(QUrl(), fileWidgetSplitter); fileWidget->setFilter(MediaWidget::extensionFilter()); fileWidget->setMode(KFile::Files | KFile::ExistingOnly); fileWidgetSplitter->setStretchFactor(1, 1); // KFileWidget creates a QUrlComboBox without layout (!), which steals the focus: // kDebug() << QApplication::focusWidget(); // kDebug() << QApplication::focusWidget()->layout(); // Let's reclaim the focus (and give it back to the main window). // FIXME report issue window()->setFocus(); } void PlaylistTab::newPlaylist() { Playlist *playlist = new Playlist(); playlist->title = i18nc("playlist browser", "Unnamed Playlist"); playlistBrowserModel->append(playlist); } void PlaylistTab::renamePlaylist() { QModelIndex index = playlistBrowserView->currentIndex(); if (!index.isValid() || (index.row() == 0)) { return; } playlistBrowserView->edit(index); } void PlaylistTab::removePlaylist() { QModelIndexList selectedRows = playlistBrowserView->selectionModel()->selectedRows(); qSort(selectedRows); for (int i = selectedRows.size() - 1; i >= 0; --i) { // FIXME compress playlistBrowserModel->removeRows(selectedRows.at(i).row(), 1); } } void PlaylistTab::savePlaylist() { savePlaylist(false); } void PlaylistTab::savePlaylistAs() { savePlaylist(true); } void PlaylistTab::addSubtitle() { QModelIndexList selectedRows = playlistView->selectionModel()->selectedRows(); if (selectedRows.size() != 1) { return; } int row = selectedRows.at(0).row(); Playlist *playlist = playlistModel->getVisiblePlaylist(); QList urls = QFileDialog::getOpenFileUrls(this, "", QUrl(), subtitleExtensionFilter()); if ((row < playlist->tracks.size()) && !urls.isEmpty()) { PlaylistTrack &track = playlist->tracks[row]; track.subtitles += urls; if (track.subtitles.size() == urls.size()) { track.currentSubtitle = 0; } if ((playlist == playlistBrowserModel->getCurrentPlaylist()) && (playlist->currentTrack == row)) { // FIXME ! } } } void PlaylistTab::playlistActivated(const QModelIndex &index) { playlistModel->setVisiblePlaylist(playlistBrowserModel->getPlaylist(index.row())); } void PlaylistTab::playPreviousTrack() { Playlist *currentPlaylist = playlistBrowserModel->getCurrentPlaylist(); playTrack(currentPlaylist, currentPlaylist->currentTrack - 1); } void PlaylistTab::playCurrentTrack() { Playlist *currentPlaylist = playlistBrowserModel->getCurrentPlaylist(); if (currentPlaylist->currentTrack >= 0) { playTrack(currentPlaylist, currentPlaylist->currentTrack); } else { playNextTrack(); } } void PlaylistTab::playNextTrack() { Playlist *currentPlaylist = playlistBrowserModel->getCurrentPlaylist(); if (randomAction->isChecked()) { int size = currentPlaylist->tracks.size(); if (size < 2) { playTrack(currentPlaylist, 0); } else if (currentPlaylist->currentTrack != -1) { int track = (qrand() % (size - 1)); if (track >= currentPlaylist->currentTrack) { ++track; } playTrack(currentPlaylist, track); } else { playTrack(currentPlaylist, qrand() % size); } } else if (((currentPlaylist->currentTrack + 1) == currentPlaylist->tracks.size()) && repeatAction->isChecked()) { playTrack(currentPlaylist, 0); } else { playTrack(currentPlaylist, currentPlaylist->currentTrack + 1); } } void PlaylistTab::activate() { mediaLayout->addWidget(mediaWidget); } void PlaylistTab::playTrack(const QModelIndex &index) { playTrack(playlistModel->getVisiblePlaylist(), index.row()); } void PlaylistTab::playTrack(Playlist *playlist, int track) { if ((track < 0) || (playlist == NULL) || (track >= playlist->tracks.size())) { track = -1; } if (track != -1) { PlaylistTrack &playlistTrack = playlist->tracks[track]; QUrl subtitleUrl; if ((playlistTrack.currentSubtitle >= 0) && (playlistTrack.currentSubtitle < playlistTrack.subtitles.size())) { subtitleUrl = playlistTrack.subtitles.at(playlistTrack.currentSubtitle); } else if (playlistTrack.subtitles.isEmpty()) { // check whether there's a possible subtitle file candidate QString localFile = playlistTrack.url.toLocalFile(); localFile.truncate(localFile.lastIndexOf(QLatin1Char('.'))); if (!localFile.isEmpty()) { if (QFile::exists(localFile + QLatin1String(".asc"))) { subtitleUrl = QUrl::fromLocalFile(localFile + QLatin1String(".asc")); } else if (QFile::exists(localFile + QLatin1String(".smi"))) { subtitleUrl = QUrl::fromLocalFile(localFile + QLatin1String(".smi")); } else if (QFile::exists(localFile + QLatin1String(".srt"))) { subtitleUrl = QUrl::fromLocalFile(localFile + QLatin1String(".srt")); } else if (QFile::exists(localFile + QLatin1String(".ssa"))) { subtitleUrl = QUrl::fromLocalFile(localFile + QLatin1String(".ssa")); } else if (QFile::exists(localFile + QLatin1String(".sub"))) { subtitleUrl = QUrl::fromLocalFile(localFile + QLatin1String(".sub")); } else if (QFile::exists(localFile + QLatin1String(".txt"))) { subtitleUrl = QUrl::fromLocalFile(localFile + QLatin1String(".txt")); } } if (subtitleUrl.isValid()) { playlistTrack.subtitles += subtitleUrl; playlistTrack.currentSubtitle = 0; } } mediaWidget->play(playlistTrack.url, subtitleUrl); // FIXME ! playlistBrowserModel->setCurrentPlaylist(playlist); } else { mediaWidget->stop(); } if (playlist != NULL) { playlistModel->setCurrentTrack(playlist, track); } } void PlaylistTab::appendUrls(const QList &urls) { playlistModel->appendUrls(playlistModel->getVisiblePlaylist(), urls, true); } void PlaylistTab::appendPlaylist(Playlist *playlist, bool playImmediately) { playlistBrowserModel->append(playlist); if (playImmediately) { playlistBrowserModel->setCurrentPlaylist(playlist); playlistModel->setVisiblePlaylist(playlist); playNextTrack(); } } void PlaylistTab::updateTrackLength(int length) { playlistModel->updateTrackLength(playlistBrowserModel->getCurrentPlaylist(), length); } void PlaylistTab::updateTrackMetadata(const QMap &metadata) { playlistModel->updateTrackMetadata(playlistBrowserModel->getCurrentPlaylist(), metadata); } QString PlaylistTab::subtitleExtensionFilter() { return QString(QLatin1String("*.asc *.smi *.srt *.ssa *.sub *.txt|")) + i18nc("file filter", "Subtitle Files"); } void PlaylistTab::savePlaylist(bool askName) { QModelIndex index = playlistBrowserView->currentIndex(); if (!index.isValid()) { return; } Playlist *playlist = playlistBrowserModel->getPlaylist(index.row()); QUrl url = playlist->url; if (askName || !url.isValid() || url.fileName().endsWith(QLatin1String(".kaffeine"), Qt::CaseInsensitive)) { url = QFileDialog::getSaveFileUrl(this, "", QUrl(), i18nc("file filter", "*.xspf|XSPF Playlist\n*.m3u|M3U Playlist\n*.pls|PLS Playlist")); if (!url.isValid()) { return; } playlist->url = url; } QString fileName = url.fileName(); Playlist::Format format = Playlist::Invalid; if (fileName.endsWith(QLatin1String(".m3u"), Qt::CaseInsensitive)) { format = Playlist::M3U; } else if (fileName.endsWith(QLatin1String(".pls"), Qt::CaseInsensitive)) { format = Playlist::PLS; } else if (fileName.endsWith(QLatin1String(".xspf"), Qt::CaseInsensitive)) { format = Playlist::XSPF; } if (format != Playlist::Invalid) { playlist->save(format); } } diff --git a/src/playlist/playlisttab.h b/src/playlist/playlisttab.h index 2b69643..fd89af2 100644 --- a/src/playlist/playlisttab.h +++ b/src/playlist/playlisttab.h @@ -1,129 +1,129 @@ /* * playlisttab.h * * Copyright (C) 2009-2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef PLAYLISTTAB_H #define PLAYLISTTAB_H #include #include "../mediawidget.h" #include "../tabbase.h" class QSplitter; class Playlist; class PlaylistBrowserView; class PlaylistModel; class PlaylistView : public QTreeView { Q_OBJECT public: explicit PlaylistView(QWidget *parent); ~PlaylistView(); public slots: void removeSelectedRows(); protected: - void contextMenuEvent(QContextMenuEvent *event); - void keyPressEvent(QKeyEvent *event); + void contextMenuEvent(QContextMenuEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; }; class PlaylistBrowserModel : public QAbstractListModel { Q_OBJECT public: PlaylistBrowserModel(PlaylistModel *playlistModel_, Playlist *temporaryPlaylist, QObject *parent); ~PlaylistBrowserModel(); void append(Playlist *playlist); Playlist *getPlaylist(int row) const; void setCurrentPlaylist(Playlist *playlist); Playlist *getCurrentPlaylist() const; - bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; signals: void playTrack(Playlist *playlist, int track); private: - int rowCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; - Qt::ItemFlags flags(const QModelIndex &index) const; - bool setData(const QModelIndex &index, const QVariant &value, int role); + int rowCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role) override; PlaylistModel *playlistModel; QList playlists; int currentPlaylist; }; class PlaylistTab : public TabBase { Q_OBJECT public: PlaylistTab(QMenu *menu, KActionCollection *collection, MediaWidget *mediaWidget_); ~PlaylistTab(); void appendToCurrentPlaylist(const QList &urls, bool playImmediately); void appendToVisiblePlaylist(const QList &urls, bool playImmediately); void removeTrack(int row); void setRandom(bool random); void setRepeat(bool repeat); int getCurrentTrack() const; int getTrackCount() const; bool getRandom() const; bool getRepeat() const; private slots: void createFileWidget(); void newPlaylist(); void renamePlaylist(); void removePlaylist(); void savePlaylist(); void savePlaylistAs(); void addSubtitle(); void playlistActivated(const QModelIndex &index); void playPreviousTrack(); void playCurrentTrack(); void playNextTrack(); void playTrack(Playlist *playlist, int track); void playTrack(const QModelIndex &index); void appendUrls(const QList &urls); void appendPlaylist(Playlist *playlist, bool playImmediately); void updateTrackLength(int length); void updateTrackMetadata(const QMap &metadata); private: static QString subtitleExtensionFilter(); // usable for KFileDialog::setFilter() - void activate(); + void activate() override; void savePlaylist(bool askName); MediaWidget *mediaWidget; QLayout *mediaLayout; QSplitter *fileWidgetSplitter; PlaylistBrowserModel *playlistBrowserModel; PlaylistBrowserView *playlistBrowserView; PlaylistModel *playlistModel; PlaylistView *playlistView; QAction *randomAction; QAction *repeatAction; }; #endif /* PLAYLISTTAB_H */ diff --git a/src/tablemodel.h b/src/tablemodel.h index 745b1a8..2531a5b 100644 --- a/src/tablemodel.h +++ b/src/tablemodel.h @@ -1,260 +1,260 @@ /* * tablemodel.h * * Copyright (C) 2011 Christoph Pfister * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef TABLEMODEL_H #define TABLEMODEL_H #include template class TableModel : public QAbstractTableModel { typedef typename T::ItemType ItemType; typedef typename T::LessThanType LessThanType; typedef typename LessThanType::SortOrder SortOrder; public: explicit TableModel(QObject *parent) : QAbstractTableModel(parent), updatingRow(-1) { } ~TableModel() { } QModelIndex find(const ItemType &item) const { if (item.isValid()) { int row = binaryFind(item); if (row < items.size()) { return index(row, 0); } } return QModelIndex(); } const ItemType &value(int row) const { if ((row >= 0) && (row < items.size())) { return items.at(row); } return sharedNull; } const ItemType &value(const QModelIndex &index) const { if ((index.row() >= 0) && (index.row() < items.size())) { return items.at(index.row()); } return sharedNull; } - int columnCount(const QModelIndex &parent) const + int columnCount(const QModelIndex &parent) const override { if (!parent.isValid()) { return helper.columnCount(); } return 0; } - int rowCount(const QModelIndex &parent) const + int rowCount(const QModelIndex &parent) const override { if (!parent.isValid()) { return items.size(); } return 0; } protected: template void reset(const U &container) { beginLayoutChange(); items.clear(); for (typename U::ConstIterator it = container.constBegin(); it != container.constEnd(); ++it) { const ItemType &item = *it; if (helper.filterAcceptsItem(item)) { items.append(item); } } qSort(items.begin(), items.end(), lessThan); endLayoutChange(); } template void resetFromKeys(const U &container) { beginLayoutChange(); items.clear(); for (typename U::ConstIterator it = container.constBegin(); it != container.constEnd(); ++it) { const ItemType &item = it.key(); if (helper.filterAcceptsItem(item)) { items.append(item); } } qSort(items.begin(), items.end(), lessThan); endLayoutChange(); } void insert(const ItemType &item) { if (item.isValid() && helper.filterAcceptsItem(item)) { int row = upperBound(item); beginInsertRows(QModelIndex(), row, row); items.insert(row, item); endInsertRows(); } } void aboutToUpdate(const ItemType &item) { updatingRow = -1; if (item.isValid()) { updatingRow = binaryFind(item); } } void update(const ItemType &item) { int row = updatingRow; updatingRow = -1; if ((row >= 0) && (row < items.size())) { if (item.isValid() && helper.filterAcceptsItem(item)) { items.replace(row, item); int targetRow = row; while (((targetRow - 1) >= 0) && lessThan(item, items.at(targetRow - 1))) { --targetRow; } while (((targetRow + 1) < items.size()) && lessThan(items.at(targetRow + 1), item)) { ++targetRow; } if (row == targetRow) { emit dataChanged(index(row, 0), index(row, helper.columnCount() - 1)); } else { beginLayoutChange(); items.move(row, targetRow); endLayoutChange(); } } else { beginRemoveRows(QModelIndex(), row, row); items.removeAt(row); endRemoveRows(); } } else { insert(item); } } void remove(const ItemType &item) { if (item.isValid()) { int row = binaryFind(item); beginRemoveRows(QModelIndex(), row, row); items.removeAt(row); endRemoveRows(); } } void internalSort(SortOrder sortOrder) { if (lessThan.getSortOrder() != sortOrder) { beginLayoutChange(); lessThan.setSortOrder(sortOrder); qSort(items.begin(), items.end(), lessThan); endLayoutChange(); } } private: int binaryFind(const ItemType &item) const { return (qBinaryFind(items.constBegin(), items.constEnd(), item, lessThan) - items.constBegin()); } int upperBound(const ItemType &item) const { return (qUpperBound(items.constBegin(), items.constEnd(), item, lessThan) - items.constBegin()); } void beginLayoutChange() { emit layoutAboutToBeChanged(); oldPersistentIndexes = persistentIndexList(); persistentItems.clear(); foreach (const QModelIndex &index, oldPersistentIndexes) { if ((index.row() >= 0) && (index.row() < items.size())) { persistentItems.append(items.at(index.row())); } else { persistentItems.append(ItemType()); } } } void endLayoutChange() { QModelIndexList newPersistentIndexes; for (int i = 0; i < oldPersistentIndexes.size(); ++i) { const QModelIndex &oldIndex = oldPersistentIndexes.at(i); const ItemType &item = persistentItems.at(i); if (item.isValid()) { int row = binaryFind(item); newPersistentIndexes.append(index(row, oldIndex.column())); } else { newPersistentIndexes.append(QModelIndex()); } } changePersistentIndexList(oldPersistentIndexes, newPersistentIndexes); oldPersistentIndexes.clear(); persistentItems.clear(); emit layoutChanged(); } private: QList items; LessThanType lessThan; ItemType sharedNull; QModelIndexList oldPersistentIndexes; QList persistentItems; int updatingRow; protected: T helper; }; #endif /* TABLEMODEL_H */