7#include "common/qohosvideooutput_p.h"
8#include "common/qohosvideosink_p.h"
10#include <private/qplatformaudiooutput_p.h>
12#include <QtMultimedia/qaudiooutput.h>
13#include <QtMultimedia/qmediaplayer.h>
14#include <QtMultimedia/qmediametadata.h>
15#include <QtMultimedia/qmediatimerange.h>
16#include <QtMultimedia/qvideosink.h>
18#include <QtCore/qfile.h>
19#include <QtCore/qfileinfo.h>
20#include <QtCore/qtemporaryfile.h>
21#include <QtCore/qloggingcategory.h>
22#include <QtCore/qmetaobject.h>
23#include <QtCore/qpointer.h>
25#include <multimedia/player_framework/native_averrors.h>
34 return QMediaPlayer::EndOfMedia;
38 return QMediaPlayer::NoMedia;
40 return QMediaPlayer::LoadingMedia;
43 return QMediaPlayer::LoadedMedia;
46 return QMediaPlayer::BufferedMedia;
48 return QMediaPlayer::EndOfMedia;
51 return QMediaPlayer::InvalidMedia;
53 return QMediaPlayer::NoMedia;
60 return QMediaPlayer::PlayingState;
62 return QMediaPlayer::PausedState;
72 return QMediaPlayer::StoppedState;
79 return AV_SPEED_FORWARD_0_125_X;
81 return AV_SPEED_FORWARD_0_25_X;
83 return AV_SPEED_FORWARD_0_50_X;
85 return AV_SPEED_FORWARD_0_75_X;
87 return AV_SPEED_FORWARD_1_00_X;
89 return AV_SPEED_FORWARD_1_25_X;
91 return AV_SPEED_FORWARD_1_50_X;
93 return AV_SPEED_FORWARD_1_75_X;
95 return AV_SPEED_FORWARD_2_00_X;
96 return AV_SPEED_FORWARD_3_00_X;
102 : QObject(parent), QPlatformMediaPlayer(parent)
118 return m_position.load();
125 OH_AVPlayer_Seek(m_player,
static_cast<int32_t>(position), AV_SEEK_NEXT_SYNC);
130 return m_bufferProgress;
142 return QMediaTimeRange{ 0, m_duration };
147 return m_playbackRate;
152 if (qFuzzyCompare(m_playbackRate, rate))
154 m_playbackRate = rate;
156 OH_AVPlayer_SetPlaybackSpeed(m_player, playbackSpeedFor(rate));
157 playbackRateChanged(rate);
176 OH_AVPlayer_Reset(m_player);
179 if (media.isEmpty()) {
182 m_pendingSetMedia =
false;
183 mediaStatusChanged(QMediaPlayer::NoMedia);
184 stateChanged(QMediaPlayer::StoppedState);
193 const QString scheme = media.scheme();
194 if (media.isLocalFile() || scheme.isEmpty() || scheme == QLatin1String(
"qrc")) {
196 if (media.isLocalFile())
197 openPath = media.toLocalFile();
198 else if (scheme == QLatin1String(
"qrc"))
199 openPath = QLatin1Char(
':') + media.path();
201 openPath = media.toString();
202 QFile probe(openPath);
203 if (!probe.open(QIODevice::ReadOnly)) {
204 mediaStatusChanged(QMediaPlayer::LoadingMedia);
205 setInvalidMediaWithError(QMediaPlayer::ResourceError,
206 QStringLiteral(
"Cannot open '%1'").arg(openPath));
216 if (m_videoOutput && m_videoSink && m_videoSink->rhi()
217 && !m_videoOutput->nativeWindow()) {
218 m_pendingSetMedia =
true;
219 mediaStatusChanged(QMediaPlayer::LoadingMedia);
223 m_pendingSetMedia =
false;
224 applyPendingSource();
229 if (!ensurePlayer()) {
230 setInvalidMediaWithError(QMediaPlayer::ResourceError,
231 QStringLiteral(
"Failed to create OH_AVPlayer"));
235 OH_AVPlayer_SetAudioRendererInfo(m_player, AUDIOSTREAM_USAGE_MOVIE);
237 m_videoSurfaceAttached =
false;
242 mediaStatusChanged(QMediaPlayer::LoadingMedia);
244 OH_AVErrCode result = AV_ERR_OK;
245 const QString scheme = m_media.scheme();
246 const bool isQrc = scheme == QLatin1String(
"qrc");
247 const bool isFileLike = m_media.isLocalFile() || scheme.isEmpty() || isQrc;
250 if (m_media.isLocalFile()) {
251 openPath = m_media.toLocalFile();
254 openPath = QLatin1Char(
':') + m_media.path();
257 openPath = m_media.toString();
259 auto file = std::make_unique<QFile>(openPath);
260 if (!file->open(QIODevice::ReadOnly)) {
261 setInvalidMediaWithError(QMediaPlayer::ResourceError,
262 QStringLiteral(
"Cannot open '%1'").arg(openPath));
265 const int fd = file->handle();
266 const qint64 size = file->size();
271 auto temp = std::make_unique<QTemporaryFile>();
272 if (!temp->open() || temp->write(file->readAll()) < 0) {
273 setInvalidMediaWithError(QMediaPlayer::ResourceError,
274 QStringLiteral(
"Cannot stage '%1'").arg(openPath));
279 const int tempFd = temp->handle();
280 const qint64 tempSize = temp->size();
281 result = OH_AVPlayer_SetFDSource(m_player, tempFd, 0, tempSize);
282 m_sourceFile = std::move(temp);
284 result = OH_AVPlayer_SetFDSource(m_player, fd, 0, size);
285 m_sourceFile = std::move(file);
288 const QByteArray url = m_media.toString(QUrl::FullyEncoded).toUtf8();
289 result = OH_AVPlayer_SetURLSource(m_player, url.constData());
292 if (result != AV_ERR_OK) {
293 qCWarning(qLcOhosMediaPlugin) <<
"Source rejected, error" <<
int(result);
294 setInvalidMediaWithError(QMediaPlayer::ResourceError,
295 QStringLiteral(
"OH_AVPlayer rejected the source"));
301 if (
auto *window = m_videoOutput->nativeWindow()) {
302 if (OH_AVPlayer_SetVideoSurface(m_player, window) == AV_ERR_OK)
303 m_videoSurfaceAttached =
true;
305 qCWarning(qLcOhosMediaPlugin) <<
"OH_AVPlayer_SetVideoSurface failed";
309 result = OH_AVPlayer_Prepare(m_player);
310 if (result != AV_ERR_OK) {
311 qCWarning(qLcOhosMediaPlugin) <<
"Prepare failed, error" <<
int(result);
312 setInvalidMediaWithError(QMediaPlayer::ResourceError,
313 QStringLiteral(
"OH_AVPlayer_Prepare failed"));
321 m_pendingPlay =
false;
325 if (m_avState == AV_PREPARED || m_avState == AV_PAUSED || m_avState == AV_COMPLETED
326 || m_avState == AV_STOPPED) {
327 m_pendingPlay =
false;
328 OH_AVPlayer_Play(m_player);
331 m_pendingPlay =
true;
337 m_pendingPlay =
false;
338 if (m_player && m_avState == AV_PLAYING)
339 OH_AVPlayer_Pause(m_player);
344 m_pendingPlay =
false;
347 if (m_avState == AV_PLAYING || m_avState == AV_PAUSED || m_avState == AV_PREPARED
348 || m_avState == AV_COMPLETED)
349 OH_AVPlayer_Stop(m_player);
354 m_audioOutput = output;
363 m_videoOutput.reset();
367 if (!m_videoOutput) {
368 m_videoOutput = std::make_unique<QOhosVideoOutput>(sink,
this);
369 connect(m_videoOutput.get(), &QOhosVideoOutput::surfaceReady,
this,
381 return m_hasVideoTrack ? 1 : 0;
383 return m_hasAudioTrack ? 1 : 0;
393 if (streamNumber != 0 || trackCount(type) == 0)
396 md.insert(QMediaMetaData::Language, QVariant::fromValue(QLocale::AnyLanguage));
404 md.insert(QMediaMetaData::Duration, m_duration);
405 if (m_videoWidth > 0 && m_videoHeight > 0)
406 md.insert(QMediaMetaData::Resolution, QSize{ m_videoWidth, m_videoHeight });
412 return trackCount(type) > 0 ? 0 : -1;
417 if (m_pendingSetMedia && !m_media.isEmpty()) {
418 m_pendingSetMedia =
false;
419 applyPendingSource();
425 if (m_avState == newState)
427 m_avState = newState;
433 if (newState == AV_IDLE)
436 if (newState == AV_PREPARED) {
437 int32_t durationMs = 0;
438 if (OH_AVPlayer_GetDuration(m_player, &durationMs) == AV_ERR_OK) {
439 if (m_duration != durationMs) {
440 m_duration = durationMs;
441 durationChanged(m_duration);
444 m_seekable = m_duration > 0;
445 seekableChanged(m_seekable);
447 int32_t videoWidth = 0;
448 int32_t videoHeight = 0;
449 OH_AVPlayer_GetVideoWidth(m_player, &videoWidth);
450 OH_AVPlayer_GetVideoHeight(m_player, &videoHeight);
451 const bool hasVideo = videoWidth > 0 && videoHeight > 0;
452 if (hasVideo && m_videoOutput)
453 m_videoOutput->setVideoSize(QSize{ videoWidth, videoHeight });
454 videoAvailableChanged(hasVideo);
455 audioAvailableChanged(
true);
456 const bool prevHasVideo = m_hasVideoTrack;
457 const bool prevHasAudio = m_hasAudioTrack;
458 m_hasVideoTrack = hasVideo;
459 m_hasAudioTrack =
true;
460 if (prevHasVideo != m_hasVideoTrack || prevHasAudio != m_hasAudioTrack) {
462 activeTracksChanged();
464 m_videoWidth = videoWidth;
465 m_videoHeight = videoHeight;
469 mediaStatusChanged(mediaStatusFor(newState,
false));
470 stateChanged(playbackStateFor(newState));
472 if (newState == AV_PREPARED && m_pendingPlay) {
473 m_pendingPlay =
false;
474 OH_AVPlayer_Play(m_player);
480 mediaStatusChanged(QMediaPlayer::EndOfMedia);
481 stateChanged(QMediaPlayer::StoppedState);
495 OH_AVPlayer_GetVideoWidth(m_player, &width);
496 OH_AVPlayer_GetVideoHeight(m_player, &height);
497 m_videoWidth = width;
498 m_videoHeight = height;
499 if (m_videoOutput && width > 0 && height > 0)
500 m_videoOutput->setVideoSize(QSize{ width, height });
501 videoAvailableChanged(width > 0 && height > 0);
506 const float progress = std::clamp(bufferingPercent / 100.0f, 0.0f, 1.0f);
507 if (qFuzzyCompare(m_bufferProgress, progress))
509 m_bufferProgress = progress;
510 bufferProgressChanged(progress);
513void QOhosMediaPlayer::handleError(int32_t errorCode,
const QString &errorMsg)
515 qCWarning(qLcOhosMediaPlugin) <<
"OH_AVPlayer error" << errorCode << errorMsg;
516 setInvalidMediaWithError(QMediaPlayer::ResourceError,
517 errorMsg.isEmpty() ? QStringLiteral(
"AVPlayer error %1").arg(errorCode)
525 OH_AVPlayer_SetOnInfoCallback(m_player,
nullptr,
nullptr);
526 OH_AVPlayer_SetOnErrorCallback(m_player,
nullptr,
nullptr);
527 OH_AVPlayer_ReleaseSync(m_player);
534 m_sourceFile.reset();
535 const bool changed = m_hasVideoTrack || m_hasAudioTrack;
536 m_hasVideoTrack =
false;
537 m_hasAudioTrack =
false;
538 const bool hadMetadata = m_duration > 0 || m_videoWidth > 0 || m_videoHeight > 0;
543 activeTracksChanged();
553 m_player = OH_AVPlayer_Create();
555 qCWarning(qLcOhosMediaPlugin) <<
"OH_AVPlayer_Create failed";
558 OH_AVPlayer_SetOnInfoCallback(m_player, &QOhosMediaPlayer::onInfoTrampoline,
this);
559 OH_AVPlayer_SetOnErrorCallback(m_player, &QOhosMediaPlayer::onErrorTrampoline,
this);
570 volume = m_audioOutput->muted ? 0.0f : m_audioOutput->volume;
572 OH_AVPlayer_SetVolume(m_player, volume, volume);
575void QOhosMediaPlayer::onInfoTrampoline(OH_AVPlayer *player, AVPlayerOnInfoType type,
576 OH_AVFormat *body,
void *userData)
585 case AV_INFO_TYPE_STATE_CHANGE: {
586 AVPlayerState state{ AV_IDLE };
587 OH_AVPlayer_GetState(player, &state);
588 QMetaObject::invokeMethod(
589 self, [self, state]() { self->handleStateChange(state); }, Qt::QueuedConnection);
592 case AV_INFO_TYPE_POSITION_UPDATE: {
594 OH_AVPlayer_GetCurrentTime(player, &pos);
595 self->m_position.store(pos);
596 QMetaObject::invokeMethod(
597 self, [self, pos]() { self->positionChanged(qint64{ pos }); },
598 Qt::QueuedConnection);
601 case AV_INFO_TYPE_DURATION_UPDATE: {
603 OH_AVPlayer_GetDuration(player, &dur);
604 QMetaObject::invokeMethod(
607 if (self->m_duration == dur)
609 self->m_duration = dur;
610 self->durationChanged(qint64{ dur });
612 Qt::QueuedConnection);
615 case AV_INFO_TYPE_EOS:
616 QMetaObject::invokeMethod(
617 self, [self]() { self->handleEndOfStream(); }, Qt::QueuedConnection);
619 case AV_INFO_TYPE_SEEKDONE:
620 QMetaObject::invokeMethod(
621 self, [self]() { self->handleSeekDone(); }, Qt::QueuedConnection);
623 case AV_INFO_TYPE_RESOLUTION_CHANGE:
624 QMetaObject::invokeMethod(
625 self, [self]() { self->handleResolutionChange(); }, Qt::QueuedConnection);
632void QOhosMediaPlayer::onErrorTrampoline(OH_AVPlayer *, int32_t errorCode,
const char *errorMsg,
638 QString message = errorMsg ? QString::fromUtf8(errorMsg) : QString{};
639 QMetaObject::invokeMethod(
640 self, [self, errorCode, message]() { self->handleError(errorCode, message); },
641 Qt::QueuedConnection);
646#include "moc_qohosmediaplayer_p.cpp"
Combined button and popup list for selecting options.
QMediaPlayer::MediaStatus mediaStatusFor(AVPlayerState state, bool atEnd)
AVPlaybackSpeed playbackSpeedFor(qreal rate)
QMediaPlayer::PlaybackState playbackStateFor(AVPlayerState state)