text: /*
   *
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied.  See the License for the
   * specific language governing permissions and limitations
   * under the License.
   *
  */
  
  #ifndef MEDIA_H_789768978
  #define MEDIA_H_789768978
  
  #include <QtMultimedia/QMediaPlayer>
  #include <QtCore>
  #include <QAudioRecorder>
  #include <QtMultimedia/QAudioEncoderSettings>
  
  #include <cplugin.h>
  #include <cordova.h>
  
  class Player;
  
  class Media: public CPlugin {
      Q_OBJECT
  public:
      explicit Media(Cordova *cordova): CPlugin(cordova) {
      }
  
      virtual const QString fullName() override {
          return Media::fullID();
      }
  
      virtual const QString shortName() override {
          return "Media";
      }
  
      static const QString fullID() {
          return "Media";
      }
  
      enum State {
          MEDIA_NONE = 0,
          MEDIA_STARTING = 1,
          MEDIA_RUNNING = 2,
          MEDIA_PAUSED = 3,
          MEDIA_STOPPED = 4
      };
      enum ErrorCode {
          MEDIA_ERR_NONE_ACTIVE = 0,
          MEDIA_ERR_ABORTED = 1,
          MEDIA_ERR_NETWORK = 2,
          MEDIA_ERR_DECODE = 3,
          MEDIA_ERR_NONE_SUPPORTED = 4
      };
  
      void execJS(const QString &js) {
          m_cordova->execJS(js);
      }
  public slots:
      void create(int scId, int ecId, const QString &id, const QString &src);
      void relase(int scId, int ecId, const QString &id);
  
      void startRecordingAudio(int scId, int ecId, const QString &id, const QString &src);
      void stopRecordingAudio(int scId, int ecId, const QString &id);
  
      void startPlayingAudio(int scId, int ecId, const QString &id, const QString &src, QVariantMap options);
      void pausePlayingAudio(int scId, int ecId, const QString &id);
      void stopPlayingAudio(int scId, int ecId, const QString &id);
      void getCurrentPositionAudio(int scId, int ecId, const QString &id);
      void seekToAudio(int scId, int ecId, const QString &id, qint64 position);
      void setVolume(int scId, int ecId, const QString &id, int volume);
  
  private:
      QMap<QString, QSharedPointer<Player> > _id2Player;
  };
  
  class Player: public QObject {
      Q_OBJECT
  public:
      Player(const QString &id, QString src, Media *plugin):
          _state(Media::MEDIA_NONE),
          _src(src),
          _mode(MODE_NONE),
          _plugin(plugin),
          _id(id),
          _stateChanged(false) {
          QUrl url(src, QUrl::TolerantMode);
  
          if (url.scheme().isEmpty()) {
              QAudioEncoderSettings audioSettings;
  
              _recorder.setEncodingSettings(audioSettings);
              _recorder.setOutputLocation(QFileInfo(src).absoluteFilePath());
  
              _player.setMedia(QUrl::fromLocalFile(QFileInfo(src).absoluteFilePath()));
          } else {
              _player.setMedia(url);
          }
          QObject::connect(&_player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), this, SLOT(onMediaStatusChanged(QMediaPlayer::MediaStatus)));
          QObject::connect(&_recorder, SIGNAL(error(QMediaRecorder::Error)), this, SLOT(onError(QMediaRecorder::Error)));
  
          connect(&_timer, SIGNAL(timeout()), this, SLOT(reportPosition()));
      }
  
      void startRecording() {
          if (recordMode() && _state != Media::MEDIA_RUNNING) {
              _recorder.record();
              setState(Media::MEDIA_RUNNING);
          }
      }
      void stopRecording() {
          if (recordMode() && _state == Media::MEDIA_RUNNING) {
              _recorder.stop();
              setState(Media::MEDIA_STOPPED);
          }
      }
  
      void setVolume(int volume) {
          _player.setVolume(volume);
      }
  
      void play() {
          if (playMode() && _state != Media::MEDIA_RUNNING) {
              _player.play();
              setState(Media::MEDIA_RUNNING);
          }
      }
      void pause() {
          if (playMode() && _state == Media::MEDIA_RUNNING) {
              _player.pause();
              setState(Media::MEDIA_PAUSED);
          }
      }
      void stop() {
          if (playMode() && (_state == Media::MEDIA_RUNNING || _state == Media::MEDIA_PAUSED)) {
              _player.stop();
              setState(Media::MEDIA_STOPPED);
          }
      }
      double getDuration() {
          if (_mode == MODE_NONE || _player.duration() == -1)
              return -1;
          if (_mode != MODE_PLAY)
              return -2;
          return static_cast<double>(_player.duration()) / 1000.0;
      }
      double getPosition() {
          if (_mode != MODE_PLAY)
              return -1;
          return static_cast<double>(_player.position()) / 1000.0;
      }
      bool seekTo(qint64 position) {
          if (!_player.isSeekable())
              return false;
          _player.setPosition(position * 1000);
          return true;
      }
  private slots:
      void reportPosition() {
          double position = getPosition();
          _plugin->execJS(QString("Media.onStatus('%1', Media.MEDIA_POSITION, %2)")
                          .arg(_id).arg(position));
          double duration = getDuration();
          _plugin->execJS(QString("Media.onStatus('%1', Media.MEDIA_DURATION, %2)")
                          .arg(_id).arg(duration));
  
          if (_stateChanged && !(_state == Media::MEDIA_RUNNING && (duration == -1 || position == 0))) {
          qCritical() << _id << "POSITION" << position << ":" << duration;
              _stateChanged = false;
              _plugin->execJS(QString("Media.onStatus('%1', Media.MEDIA_STATE, %2)").arg(_id).arg(_state));
          }
      }
  
      void onMediaStatusChanged(QMediaPlayer::MediaStatus status) {
          if (status == QMediaPlayer::InvalidMedia) {
              reportError(Media::MEDIA_ERR_ABORTED, "AudioPlayer Error: The current media cannot be played.");
              setState(Media::MEDIA_STOPPED);
          }
          if (status == QMediaPlayer::EndOfMedia) {
              setState(Media::MEDIA_STOPPED);
              seekTo(0);
          }
      }
      void onError(QMediaRecorder::Error) {
          reportError(Media::MEDIA_ERR_NONE_SUPPORTED, "AudioPlayer Error: Device is not ready or not available.");
          setState(Media::MEDIA_STOPPED);
      }
  
  private:
      void reportError(int code, const QString &descr) {
          Q_UNUSED(descr);
          _plugin->execJS(QString("Media.onStatus('%1', Media.MEDIA_ERROR, {code: %2})")
                          .arg(_id).arg(code));
      }
  
      bool playMode() {
          switch (_mode) {
          case Player::MODE_NONE:
              _mode = MODE_PLAY;
              break;
          case Player::MODE_PLAY:
              break;
          case Player::MODE_RECORD:
              reportError(Media::MEDIA_ERR_NONE_SUPPORTED, "AudioPlayer Error: Can't play in record mode.");
              return false;
              break;
          }
          return true;
      }
  
      bool recordMode() {
          switch (_mode) {
          case Player::MODE_NONE:
              if (_recorder.outputLocation().isEmpty()) {
                  reportError(Media::MEDIA_ERR_NONE_SUPPORTED, "AudioPlayer Error: unsupported output location.");
                  return false;
              }
              _mode = MODE_RECORD;
              break;
          case Player::MODE_PLAY:
              reportError(Media::MEDIA_ERR_NONE_SUPPORTED, "AudioPlayer Error: Can't play in play mode.");
              return false;
              break;
          case Player::MODE_RECORD:
              break;
          }
          return true;
      }
  
      void setState(Media::State state) {
          _state = state;
          _stateChanged = true;
          _timer.start(250);
      }
  
      QMediaPlayer _player;
  
      QAudioRecorder _recorder;
      QTimer _timer;
  
      Media::State _state;
      QString _src;
      enum Mode {
          MODE_NONE,
          MODE_PLAY,
          MODE_RECORD
      };
      Mode _mode;
      Media *_plugin;
      QString _id;
  
      bool _stateChanged;
  };
  
  #endif
  