blob: c1f37122847f071c5ceb2380fc6538a58744a5eb [file] [log] [blame]
/*
*
* 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