/* * * 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 #include #include #include #include #include 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 > _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(_player.duration()) / 1000.0; } double getPosition() { if (_mode != MODE_PLAY) return -1; return static_cast(_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