Skip to content
Snippets Groups Projects
Select Git revision
  • 6b874a99ac5f18d978d4ed0a0492f34295e0b92f
  • master default protected
  • release/202005
  • release/202001
  • release/201912
  • release/201911
  • release/releaseWindowsTestOne
  • release/releaseTest
  • release/releaseWindowsTest
  • release/windowsReleaseTest
  • release/201910
  • release/qt/201910
  • release/windows-test/201910
  • release/201908
  • release/201906
  • release/201905
  • release/201904
  • release/201903
  • release/201902
  • release/201901
  • release/201812
  • 1.0.0
  • 0.3.0
  • 0.2.1
  • 0.2.0
  • 0.1.0
26 results

codecmodel.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    codecmodel.cpp 23.99 KiB
    /****************************************************************************
     *   Copyright (C) 2012-2018 Savoir-faire Linux                          *
     *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> *
     *                                                                          *
     *   This library is free software; you can redistribute it and/or          *
     *   modify it under the terms of the GNU Lesser General Public             *
     *   License as published by the Free Software Foundation; either           *
     *   version 2.1 of the License, or (at your option) any later version.     *
     *                                                                          *
     *   This library 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      *
     *   Lesser General Public License for more details.                        *
     *                                                                          *
     *   You should have received a copy of the GNU General Public License      *
     *   along with this program.  If not, see <http://www.gnu.org/licenses/>.  *
     ***************************************************************************/
    #include "codecmodel.h"
    
    //Qt
    #include <QtCore/QDebug>
    #include <QtCore/QCoreApplication>
    #include <QtCore/QSortFilterProxyModel>
    #include <QtCore/QItemSelectionModel>
    #include <QMimeData>
    
    //DRing
    #include <account_const.h>
    
    //Ring
    #include "account.h"
    #include "dbus/configurationmanager.h"
    #include "mime.h"
    #include "callmodel.h"
    #include <private/matrixutils.h>
    
    typedef void (CodecModelPrivate::*CodecModelFct)();
    
    class CodecModelPrivate final : public QObject
    {
       Q_OBJECT
    public:
       CodecModelPrivate(CodecModel* parent);
       ///@struct CodecData store audio/video codec information
       struct CodecData {
          int              id         ;
          QString          name       ;
          QString          bitrate    ;
          QString          min_bitrate;
          QString          max_bitrate;
          QString          samplerate ;
          QString          type       ;
          QString          quality    ;
          QString          min_quality;
          QString          max_quality;
          QString          auto_quality_enabled;
       };
    
       //Attributes
       QList<CodecData*>      m_lCodecs        ;
       QMap<int,bool>         m_lEnabledCodecs ;
       Account*               m_pAccount       ;
       QSortFilterProxyModel* m_pAudioProxy    ;
       QSortFilterProxyModel* m_pVideoProxy    ;
       QStringList            m_lMimes         ;
       QItemSelectionModel*   m_pSelectionModel;
       CodecModel::EditState  m_EditState      ;
       static Matrix2D<CodecModel::EditState, CodecModel::EditAction,CodecModelFct> m_mStateMachine;
    
       //Callbacks
       QModelIndex add         (                        );
       void        remove      ( const QModelIndex& idx );
       void        clear       (                        );
       void        reload      (                        );
       void        save        (                        );
       void        nothing     (                        );
       void        modify      (                        );
    
       //Helpers
       bool findCodec(int id);
       QModelIndex getIndexofCodecByID(int id);
       inline void performAction(const CodecModel::EditAction action);
    
    private:
       CodecModel* q_ptr;
    };
    
    #define CMP &CodecModelPrivate
    Matrix2D<CodecModel::EditState, CodecModel::EditAction,CodecModelFct> CodecModelPrivate::m_mStateMachine ={{
       /*                     SAVE         MODIFY        RELOAD        CLEAR      */
       /* LOADING   */ {{ CMP::nothing, CMP::nothing, CMP::reload , CMP::nothing  }},
       /* READY     */ {{ CMP::nothing, CMP::modify , CMP::reload , CMP::clear    }},
       /* MODIFIED  */ {{ CMP::save   , CMP::nothing, CMP::reload , CMP::clear    }},
       /* OUTDATED  */ {{ CMP::save   , CMP::nothing, CMP::reload , CMP::clear    }},
       /* RELOADING */ {{ CMP::nothing, CMP::nothing, CMP::nothing, CMP::nothing  }},
    }};
    #undef CMP
    
    CodecModelPrivate::CodecModelPrivate(CodecModel* parent) : q_ptr(parent),
    m_pAudioProxy(nullptr),m_pVideoProxy(nullptr),m_pSelectionModel(nullptr)
    {
    
    }
    
    ///Constructor
    CodecModel::CodecModel(Account* account) :
    QAbstractListModel(account?(QObject*)account:(QObject*)QCoreApplication::instance()), d_ptr(new CodecModelPrivate(this))
    {
       Q_ASSERT(account);
       d_ptr->m_EditState = CodecModel::EditState::LOADING;
       d_ptr->m_pAccount = account;
    
       // New accounts don't have ID yet, avoid a warning
       if (account && !account->isNew())
          setObjectName("CodecModel: "+(account?account->id():"Unknown"));
    
       d_ptr->m_lMimes << RingMimes::AUDIO_CODEC << RingMimes::VIDEO_CODEC;
       this << EditAction::RELOAD;
       d_ptr->m_EditState = CodecModel::EditState::READY;
    }
    
    CodecModel::~CodecModel()
    {
       while (d_ptr->m_lCodecs.size()) {
          CodecModelPrivate::CodecData* c = d_ptr->m_lCodecs[0];
          d_ptr->m_lCodecs.removeAt(0);
          delete c;
       }
    }
    
    QHash<int,QByteArray> CodecModel::roleNames() const
    {
       static QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
       static bool initRoles = false;
       if (!initRoles) {
          initRoles = true;
          roles.insert(CodecModel::Role::ID         ,QByteArray("id"));
          roles.insert(CodecModel::Role::NAME       ,QByteArray("name"));
          roles.insert(CodecModel::Role::BITRATE    ,QByteArray("bitrate"));
          roles.insert(CodecModel::Role::MIN_BITRATE,QByteArray("min_bitrate"));
          roles.insert(CodecModel::Role::MAX_BITRATE,QByteArray("max_bitrate"));
          roles.insert(CodecModel::Role::SAMPLERATE ,QByteArray("samplerate"));
          roles.insert(CodecModel::Role::TYPE       ,QByteArray("type"));
          roles.insert(CodecModel::Role::QUALITY    ,QByteArray("quality"));
          roles.insert(CodecModel::Role::MIN_QUALITY,QByteArray("min_quality"));
          roles.insert(CodecModel::Role::MAX_QUALITY,QByteArray("max_quality"));
          roles.insert(CodecModel::Role::AUTO_QUALITY_ENABLED,QByteArray("autoQualityEnabled"));
       }
       return roles;
    }
    
    QItemSelectionModel* CodecModel::selectionModel() const
    {
       if (!d_ptr->m_pSelectionModel)
          d_ptr->m_pSelectionModel = new QItemSelectionModel(const_cast<CodecModel*>(this));
       return d_ptr->m_pSelectionModel;
    }
    
    ///Model data
    QVariant CodecModel::data(const QModelIndex& idx, int role) const
    {
        if (idx.column() != 0)
            return QVariant();
    
        switch (role) {
            case Qt::DisplayRole:
                return QVariant(d_ptr->m_lCodecs[idx.row()]->name);
            case Qt::CheckStateRole:
                return QVariant(d_ptr->m_lEnabledCodecs[d_ptr->m_lCodecs[idx.row()]->id] ? Qt::Checked : Qt::Unchecked);
            case CodecModel::Role::NAME:
                return d_ptr->m_lCodecs[idx.row()]->name;
            case CodecModel::Role::BITRATE:
                return d_ptr->m_lCodecs[idx.row()]->bitrate;
            case CodecModel::Role::MIN_BITRATE:
                return d_ptr->m_lCodecs[idx.row()]->min_bitrate;
            case CodecModel::Role::MAX_BITRATE:
                return d_ptr->m_lCodecs[idx.row()]->max_bitrate;
            case CodecModel::Role::SAMPLERATE:
                return d_ptr->m_lCodecs[idx.row()]->samplerate;
            case CodecModel::Role::ID:
                return d_ptr->m_lCodecs[idx.row()]->id;
            case CodecModel::Role::TYPE:
                return d_ptr->m_lCodecs[idx.row()]->type;
            case CodecModel::Role::QUALITY:
                return d_ptr->m_lCodecs[idx.row()]->quality;
            case CodecModel::Role::MIN_QUALITY:
                return d_ptr->m_lCodecs[idx.row()]->min_quality;
            case CodecModel::Role::MAX_QUALITY:
                return d_ptr->m_lCodecs[idx.row()]->max_quality;
            case CodecModel::Role::AUTO_QUALITY_ENABLED:
                return d_ptr->m_lCodecs[idx.row()]->auto_quality_enabled;
            default:
                return QVariant();
        }
        return QVariant();
    }
    
    ///Number of audio codecs
    int CodecModel::rowCount(const QModelIndex& par) const
    {
       return par.isValid() ? 0 : d_ptr->m_lCodecs.size();
    }
    
    ///Model flags
    Qt::ItemFlags CodecModel::flags(const QModelIndex& idx) const
    {
       if (idx.column() == 0)
          return QAbstractItemModel::flags(idx) | Qt::ItemIsUserCheckable
                                                | Qt::ItemIsEnabled
                                                | Qt::ItemIsSelectable
                                                | Qt::ItemIsDragEnabled
                                                | Qt::ItemIsDropEnabled;
       return QAbstractItemModel::flags(idx);
    }
    
    ///Set audio codec data
    bool CodecModel::setData( const QModelIndex& idx, const QVariant &value, int role)
    {
        if (idx.column() != 0)
            return false;
    
        switch (role) {
            case CodecModel::NAME :
                d_ptr->m_lCodecs[idx.row()]->name = value.toString();
                break;
            case CodecModel::BITRATE :
                d_ptr->m_lCodecs[idx.row()]->bitrate = value.toString();
                break;
            case CodecModel::MIN_BITRATE :
                d_ptr->m_lCodecs[idx.row()]->min_bitrate = value.toString();
                break;
            case CodecModel::MAX_BITRATE :
                d_ptr->m_lCodecs[idx.row()]->max_bitrate = value.toString();
                break;
            case Qt::CheckStateRole :
                d_ptr->m_lEnabledCodecs[d_ptr->m_lCodecs[idx.row()]->id] = value.toBool();
                break;
            case CodecModel::SAMPLERATE :
                d_ptr->m_lCodecs[idx.row()]->samplerate = value.toString();
                break;
            case CodecModel::ID :
                d_ptr->m_lCodecs[idx.row()]->id = value.toInt();
                break;
            case CodecModel::TYPE :
                d_ptr->m_lCodecs[idx.row()]->type = value.toString();
                break;
            case CodecModel::QUALITY :
                d_ptr->m_lCodecs[idx.row()]->quality = value.toString();
                break;
            case CodecModel::MIN_QUALITY :
                d_ptr->m_lCodecs[idx.row()]->min_quality = value.toString();
                break;
            case CodecModel::MAX_QUALITY :
                d_ptr->m_lCodecs[idx.row()]->max_quality = value.toString();
                break;
            case CodecModel::AUTO_QUALITY_ENABLED :
                d_ptr->m_lCodecs[idx.row()]->auto_quality_enabled = value.toString();
                break;
            default:
                return false;
        }
    
        //if we did not return yet, then we modified the codec
        emit dataChanged(idx, idx);
        this << EditAction::MODIFY;
        return true;
    }
    
    ///Add a new audio codec
    QModelIndex CodecModelPrivate::add()
    {
       q_ptr->beginInsertRows(QModelIndex(), m_lCodecs.size()-1, m_lCodecs.size()-1);
       m_lCodecs << new CodecModelPrivate::CodecData;
       q_ptr->endInsertRows();
       emit q_ptr->dataChanged(q_ptr->index(m_lCodecs.size()-1,0), q_ptr->index(m_lCodecs.size()-1,0));
       q_ptr << CodecModel::EditAction::MODIFY;
       return q_ptr->index(m_lCodecs.size()-1,0);
    }
    
    ///Remove audio codec at 'idx'
    void CodecModelPrivate::remove(const QModelIndex& idx)
    {
       if (idx.isValid()) {
          q_ptr->beginRemoveRows(QModelIndex(), idx.row(), idx.row());
          CodecModelPrivate::CodecData* d = m_lCodecs[idx.row()];
          m_lCodecs.removeAt(idx.row());
          delete d;
          q_ptr->endRemoveRows();
          emit q_ptr->dataChanged(idx, q_ptr->index(m_lCodecs.size()-1,0));
          q_ptr << CodecModel::EditAction::MODIFY;
       }
       else {
          qDebug() << "Failed to remove an invalid audio codec";
       }
    }
    
    ///Remove everything
    void CodecModelPrivate::clear()
    {
       while(m_lCodecs.size()) {
          CodecModelPrivate::CodecData* d = m_lCodecs[0];
          m_lCodecs.removeAt(0);
          delete d;
       }
       m_lCodecs.clear();
       m_lEnabledCodecs.clear();
       m_EditState = CodecModel::EditState::READY;
    }
    
    ///Reload the codeclist
    void CodecModelPrivate::reload()
    {
       m_EditState = CodecModel::EditState::RELOADING;
    
       ConfigurationManagerInterface& configurationManager = ConfigurationManager::instance();
       QVector<uint> codecIdList = configurationManager.getCodecList();
    
       QVector<uint> activeCodecList = m_pAccount->isNew() ? codecIdList :
          configurationManager.getActiveCodecList(m_pAccount->id());
    
       QStringList tmpNameList;
    
       //TODO: the following method cannot update the order of the codecs if it
       //      changes in the daemon after it has been initially loaded in the client
    
       // load the active codecs first to get the correct order
       foreach (const int aCodec, activeCodecList) {
    
          const QMap<QString,QString> codec = configurationManager.getCodecDetails(
             m_pAccount->isNew()? QString() : m_pAccount->id(), aCodec
          );
    
          if (!findCodec(aCodec)) {
             const QModelIndex& idx = add();
             q_ptr->setData(idx,QString::number(aCodec), CodecModel::Role::ID);
          }
    
          // update the codec
    
          const auto& idx = getIndexofCodecByID(aCodec);
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::NAME        ] ,CodecModel::Role::NAME       );
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::SAMPLE_RATE ] ,CodecModel::Role::SAMPLERATE );
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::BITRATE     ] ,CodecModel::Role::BITRATE    );
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::MIN_BITRATE ] ,CodecModel::Role::MIN_BITRATE);
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::MAX_BITRATE ] ,CodecModel::Role::MAX_BITRATE);
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::TYPE        ] ,CodecModel::Role::TYPE       );
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::QUALITY     ] ,CodecModel::Role::QUALITY    );
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::MIN_QUALITY ] ,CodecModel::Role::MIN_QUALITY);
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::MAX_QUALITY ] ,CodecModel::Role::MAX_QUALITY);
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::AUTO_QUALITY_ENABLED] ,CodecModel::Role::AUTO_QUALITY_ENABLED);
          q_ptr->setData(idx, Qt::Checked ,Qt::CheckStateRole);
    
          // remove from list of all codecs, since we have already updated it
          if (codecIdList.indexOf(aCodec)!=-1)
             codecIdList.remove(codecIdList.indexOf(aCodec));
       }
    
       // now add add/update remaining (inactive) codecs
       foreach (const int aCodec, codecIdList) {
    
          const QMap<QString,QString> codec = configurationManager.getCodecDetails(
             m_pAccount->isNew()? QString() : m_pAccount->id(), aCodec
          );
    
          if (!findCodec(aCodec)) {
             const QModelIndex& idx = add();
             q_ptr->setData(idx,QString::number(aCodec), CodecModel::Role::ID);
          }
    
          // update the codec
          const auto& idx = getIndexofCodecByID(aCodec);
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::NAME        ] ,CodecModel::Role::NAME       );
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::SAMPLE_RATE ] ,CodecModel::Role::SAMPLERATE );
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::BITRATE     ] ,CodecModel::Role::BITRATE    );
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::MIN_BITRATE ] ,CodecModel::Role::MIN_BITRATE);
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::MAX_BITRATE ] ,CodecModel::Role::MAX_BITRATE);
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::TYPE        ] ,CodecModel::Role::TYPE       );
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::QUALITY     ] ,CodecModel::Role::QUALITY    );
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::MIN_QUALITY ] ,CodecModel::Role::MIN_QUALITY);
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::MAX_QUALITY ] ,CodecModel::Role::MAX_QUALITY);
          q_ptr->setData(idx,codec[ DRing::Account::ConfProperties::CodecInfo::AUTO_QUALITY_ENABLED] ,CodecModel::Role::AUTO_QUALITY_ENABLED);
          q_ptr->setData(idx, Qt::Unchecked ,Qt::CheckStateRole);
       }
    
       m_EditState = CodecModel::EditState::READY;
    }
    
    ///Save details
    void CodecModelPrivate::save()
    {
       //TODO there is a race condition, the account has to be saved first
    
       //Update active codec list
       VectorUInt _codecList;
       for (int i = 0; i < q_ptr->rowCount();i++) {
          const QModelIndex& idx = q_ptr->index(i,0);
          if (q_ptr->data(idx,Qt::CheckStateRole) == Qt::Checked) {
             _codecList << q_ptr->data(idx,CodecModel::Role::ID).toInt();
          }
       }
    
       ConfigurationManagerInterface& configurationManager = ConfigurationManager::instance();
       configurationManager.setActiveCodecList(m_pAccount->id(), _codecList);
    
       //Update codec details
       for (int i=0; i < q_ptr->rowCount();i++) {
          const QModelIndex& idx = q_ptr->index(i,0);
          MapStringString codecDetails;
          codecDetails[ DRing::Account::ConfProperties::CodecInfo::NAME        ] = q_ptr->data(idx,CodecModel::Role::NAME).toString();
          codecDetails[ DRing::Account::ConfProperties::CodecInfo::SAMPLE_RATE ] = q_ptr->data(idx,CodecModel::Role::SAMPLERATE).toString();
          codecDetails[ DRing::Account::ConfProperties::CodecInfo::BITRATE     ] = q_ptr->data(idx,CodecModel::Role::BITRATE).toString();
          codecDetails[ DRing::Account::ConfProperties::CodecInfo::MIN_BITRATE ] = q_ptr->data(idx,CodecModel::Role::MIN_BITRATE).toString();
          codecDetails[ DRing::Account::ConfProperties::CodecInfo::MAX_BITRATE ] = q_ptr->data(idx,CodecModel::Role::MAX_BITRATE).toString();
          codecDetails[ DRing::Account::ConfProperties::CodecInfo::TYPE        ] = q_ptr->data(idx,CodecModel::Role::TYPE).toString();
          codecDetails[ DRing::Account::ConfProperties::CodecInfo::QUALITY     ] = q_ptr->data(idx,CodecModel::Role::QUALITY).toString();
          codecDetails[ DRing::Account::ConfProperties::CodecInfo::MIN_QUALITY ] = q_ptr->data(idx,CodecModel::Role::MIN_QUALITY).toString();
          codecDetails[ DRing::Account::ConfProperties::CodecInfo::MAX_QUALITY ] = q_ptr->data(idx,CodecModel::Role::MAX_QUALITY).toString();
          codecDetails[ DRing::Account::ConfProperties::CodecInfo::AUTO_QUALITY_ENABLED] = q_ptr->data(idx,CodecModel::Role::AUTO_QUALITY_ENABLED).toString();
    
          qDebug() << "setting codec details for " << q_ptr->data(idx,CodecModel::Role::NAME).toString();
    
          configurationManager.setCodecDetails(m_pAccount->id(), q_ptr->data(idx,CodecModel::Role::ID).toUInt(), codecDetails);
       }
       m_EditState = CodecModel::EditState::READY;
    }
    
    void CodecModelPrivate::nothing()
    {
       //nothing
    }
    
    void CodecModelPrivate::modify()
    {
       m_EditState = CodecModel::EditState::MODIFIED;
       m_pAccount << Account::EditAction::MODIFY;
    }
    
    void CodecModelPrivate::performAction(const CodecModel::EditAction action)
    {
       (this->*(m_mStateMachine[m_EditState][action]))();//FIXME don't use integer cast
    }
    
    CodecModel* CodecModel::operator<<(CodecModel::EditAction& action)
    {
       performAction(action);
       return this;
    }
    
    CodecModel* operator<<(CodecModel* a, CodecModel::EditAction action)
    {
       return (!a)?nullptr : (*a) << action;
    }
    
    ///Change the current edition state
    bool CodecModel::performAction(const CodecModel::EditAction action)
    {
       CodecModel::EditState curState = d_ptr->m_EditState;
       d_ptr->performAction(action);
       return curState != d_ptr->m_EditState;
    }
    
    ///Move a codec up in the priority list (using the selectionModel)
    bool CodecModel::moveUp()
    {
       if (d_ptr->m_pSelectionModel) {
          const QModelIndex& idx = d_ptr->m_pSelectionModel->currentIndex();
          if (dropMimeData(mimeData({idx}), Qt::MoveAction, idx.row()-1, idx.column(),idx.parent())) {
             d_ptr->m_pSelectionModel->setCurrentIndex(index(idx.row()-1,idx.column()), QItemSelectionModel::ClearAndSelect);
             return true;
          }
       }
       return false;
    }
    
    ///Move a codec down in the priority list (using the selectionModel)
    bool CodecModel::moveDown()
    {
       if (d_ptr->m_pSelectionModel) {
          const QModelIndex& idx = d_ptr->m_pSelectionModel->currentIndex();
          if (dropMimeData(mimeData({idx}), Qt::MoveAction, idx.row()+1, idx.column(),idx.parent())) {
             d_ptr->m_pSelectionModel->setCurrentIndex(index(idx.row()+1,idx.column()), QItemSelectionModel::ClearAndSelect);
             return true;
          }
       }
       return false;
    }
    
    ///Check is a codec is already in the list
    bool CodecModelPrivate::findCodec(int id)
    {
       foreach(const CodecData* data, m_lCodecs) {
          if (data->id == id)
             return true;
       }
       return false;
    }
    
    ///Return valid payload types
    int CodecModel::acceptedPayloadTypes() const
    {
       return CallModel::DropPayloadType::AUDIO_CODEC | CallModel::DropPayloadType::VIDEO_CODEC;
    }
    
    
    QSortFilterProxyModel* CodecModel::videoCodecs() const
    {
       if (!d_ptr->m_pVideoProxy) {
          d_ptr->m_pVideoProxy = new QSortFilterProxyModel(const_cast<CodecModel*>(this));
          d_ptr->m_pVideoProxy->setSourceModel(const_cast<CodecModel*>(this));
          d_ptr->m_pVideoProxy->setFilterRole(CodecModel::Role::TYPE);
          d_ptr->m_pVideoProxy->setFilterFixedString("VIDEO");
       }
       return d_ptr->m_pVideoProxy;
    }
    
    
    QSortFilterProxyModel* CodecModel::audioCodecs() const
    {
       if (!d_ptr->m_pAudioProxy) {
          d_ptr->m_pAudioProxy = new QSortFilterProxyModel(const_cast<CodecModel*>(this));
          d_ptr->m_pAudioProxy->setSourceModel(const_cast<CodecModel*>(this));
          d_ptr->m_pAudioProxy->setFilterRole(CodecModel::Role::TYPE);
          d_ptr->m_pAudioProxy->setFilterFixedString("AUDIO");
       }
       return d_ptr->m_pAudioProxy;
    }
    
    // ======== DnD Api =========
    
    QModelIndex CodecModelPrivate::getIndexofCodecByID(int id)
    {
       for (int i=0; i < q_ptr->rowCount();i++) {
          const QModelIndex& idx = q_ptr->index(i,0);
          if (q_ptr->data(idx, CodecModel::Role::ID) == id)
             return idx;
       }
       return QModelIndex();
    }
    
    QStringList CodecModel::mimeTypes() const
    {
       return d_ptr->m_lMimes;
    }
    
    bool CodecModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent)
    {
       Q_UNUSED(action)
       if(parent.isValid() || column > 0) {
          qDebug() << "column invalid";
          return false;
       }
    
       if (data->hasFormat(RingMimes::AUDIO_CODEC) || data->hasFormat(RingMimes::VIDEO_CODEC)) {
          int destinationRow = -1;
    
          if(row < 0) {
             //drop on top
             destinationRow = d_ptr->m_lCodecs.size() - 1;
          }
          else if(row >= d_ptr->m_lCodecs.size()) {
             destinationRow = 0;
          }
          else if(data->hasFormat(RingMimes::VIDEO_CODEC) && row >= rowCount()) {
             destinationRow = 0;
          }
          else {
             destinationRow = row;
          }
    
          const int codecId = data->data(data->hasFormat(RingMimes::AUDIO_CODEC)
                                                        ? RingMimes::AUDIO_CODEC
                                                        : RingMimes::VIDEO_CODEC).toInt();
    
          const QModelIndex codecIdx = d_ptr->getIndexofCodecByID(codecId);
    
    #ifdef Q_OS_WIN
          /* To understand why qtDestinationRow is needed
          http://doc.qt.io/qt-5/qabstractitemmodel.html#beginMoveRows */
          auto qtDestinationRow = destinationRow > codecIdx.row() ? destinationRow+1 : destinationRow;
          beginMoveRows(parent, codecIdx.row(), codecIdx.row(), parent, qtDestinationRow);
          d_ptr->m_lCodecs.move(codecIdx.row(), destinationRow);
          endMoveRows();
    #else
          beginRemoveRows(QModelIndex(), codecIdx.row(), codecIdx.row());
          CodecModelPrivate::CodecData* codecInfo = d_ptr->m_lCodecs[codecIdx.row()];
          d_ptr->m_lCodecs.removeAt(codecIdx.row());
          endRemoveRows();
    
          beginInsertRows(QModelIndex(), destinationRow, destinationRow);
          d_ptr->m_lCodecs.insert(destinationRow,codecInfo);
          endInsertRows();
    #endif
    
          this << EditAction::MODIFY;
    
          return true;
       }
    
       return false;
    }
    
    QMimeData* CodecModel::mimeData(const QModelIndexList& indexes) const
    {
       QMimeData* mMimeData = new QMimeData();
    
       for (const QModelIndex& index : indexes) {
          if (index.isValid()) {
             qDebug() << "setting mime data for row: " << index.row();
    
             const QByteArray mime = (index.data(CodecModel::Role::TYPE).toString() == "AUDIO")
                ? RingMimes::AUDIO_CODEC
                : RingMimes::VIDEO_CODEC;
    
             mMimeData->setData(mime, index.data(CodecModel::Role::ID).toByteArray());
          }
       }
    
       return mMimeData;
    }
    
    Qt::DropActions CodecModel::supportedDragActions() const
    {
       return Qt::MoveAction | Qt::TargetMoveAction;
    }
    
    Qt::DropActions CodecModel::supportedDropActions() const
    {
       return Qt::MoveAction | Qt::TargetMoveAction;
    }
    
    #include <codecmodel.moc>