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/qtimer.h>
20#include <QtCore/qfileinfo.h>
21#include <QtCore/qtemporaryfile.h>
22#include <QtCore/qloggingcategory.h>
23#include <QtCore/qmetaobject.h>
24#include <QtCore/qpointer.h>
26#include <multimedia/player_framework/native_averrors.h>
34QMediaPlayer::MediaStatus mediaStatusFor(AVPlayerState state,
bool atEnd)
37 return QMediaPlayer::EndOfMedia;
41 return QMediaPlayer::NoMedia;
43 return QMediaPlayer::LoadingMedia;
46 return QMediaPlayer::LoadedMedia;
49 return QMediaPlayer::BufferedMedia;
51 return QMediaPlayer::EndOfMedia;
54 return QMediaPlayer::InvalidMedia;
56 return QMediaPlayer::NoMedia;
59QMediaPlayer::PlaybackState playbackStateFor(AVPlayerState state)
63 return QMediaPlayer::PlayingState;
65 return QMediaPlayer::PausedState;
75 return QMediaPlayer::StoppedState;
78AVPlaybackSpeed playbackSpeedFor(qreal rate)
82 return AV_SPEED_FORWARD_0_125_X;
84 return AV_SPEED_FORWARD_0_25_X;
86 return AV_SPEED_FORWARD_0_50_X;
88 return AV_SPEED_FORWARD_0_75_X;
90 return AV_SPEED_FORWARD_1_00_X;
92 return AV_SPEED_FORWARD_1_25_X;
94 return AV_SPEED_FORWARD_1_50_X;
96 return AV_SPEED_FORWARD_1_75_X;
98 return AV_SPEED_FORWARD_2_00_X;
99 return AV_SPEED_FORWARD_3_00_X;
105 : QObject(parent), QPlatformMediaPlayer(parent)
121 return m_position.load();
128 OH_AVPlayer_Seek(m_player,
static_cast<int32_t>(position), AV_SEEK_NEXT_SYNC);
133 return m_bufferProgress;
145 return QMediaTimeRange{ 0, m_duration };
150 return m_playbackRate;
155 if (qFuzzyCompare(m_playbackRate, rate))
157 m_playbackRate = rate;
159 OH_AVPlayer_SetPlaybackSpeed(m_player, playbackSpeedFor(rate));
160 playbackRateChanged(rate);
179 OH_AVPlayer_Reset(m_player);
182 if (media.isEmpty()) {
185 m_pendingSetMedia =
false;
186 mediaStatusChanged(QMediaPlayer::NoMedia);
187 stateChanged(QMediaPlayer::StoppedState);
196 const QString scheme = media.scheme();
197 if (media.isLocalFile() || scheme.isEmpty() || scheme == QLatin1String(
"qrc")) {
199 if (media.isLocalFile())
200 openPath = media.toLocalFile();
201 else if (scheme == QLatin1String(
"qrc"))
202 openPath = QLatin1Char(
':') + media.path();
204 openPath = media.toString();
205 QFile probe(openPath);
206 if (!probe.open(QIODevice::ReadOnly)) {
207 mediaStatusChanged(QMediaPlayer::LoadingMedia);
208 setInvalidMediaWithError(QMediaPlayer::ResourceError,
209 QStringLiteral(
"Cannot open '%1'").arg(openPath));
214 m_pendingSetMedia =
false;
215 m_sourceApplied =
false;
216 m_surfaceWaitStarted =
false;
219 QMetaObject::invokeMethod(
this, &QOhosMediaPlayer::tryApplyPendingSource,
220 Qt::QueuedConnection);
225 if (m_sourceApplied || m_media.isEmpty())
231 if (m_videoOutput && m_videoSink && !m_videoSink->rhi()) {
232 if (!m_surfaceWaitStarted) {
233 m_surfaceWaitStarted =
true;
234 QTimer::singleShot(surfaceWaitTimeoutMs,
this, [
this] {
235 if (!m_sourceApplied && m_pendingSetMedia && !m_media.isEmpty()) {
236 m_pendingSetMedia =
false;
237 m_sourceApplied =
true;
238 applyPendingSource();
242 m_pendingSetMedia =
true;
243 mediaStatusChanged(QMediaPlayer::LoadingMedia);
247 m_sourceApplied =
true;
248 applyPendingSource();
253 if (!ensurePlayer()) {
254 setInvalidMediaWithError(QMediaPlayer::ResourceError,
255 QStringLiteral(
"Failed to create OH_AVPlayer"));
259 OH_AVPlayer_SetAudioRendererInfo(m_player, AUDIOSTREAM_USAGE_MOVIE);
261 m_videoSurfaceAttached =
false;
266 mediaStatusChanged(QMediaPlayer::LoadingMedia);
268 OH_AVErrCode result = AV_ERR_OK;
269 const QString scheme = m_media.scheme();
270 const bool isQrc = scheme == QLatin1String(
"qrc");
271 const bool isFileLike = m_media.isLocalFile() || scheme.isEmpty() || isQrc;
274 if (m_media.isLocalFile()) {
275 openPath = m_media.toLocalFile();
278 openPath = QLatin1Char(
':') + m_media.path();
281 openPath = m_media.toString();
283 auto file = std::make_unique<QFile>(openPath);
284 if (!file->open(QIODevice::ReadOnly)) {
285 setInvalidMediaWithError(QMediaPlayer::ResourceError,
286 QStringLiteral(
"Cannot open '%1'").arg(openPath));
289 const int fd = file->handle();
290 const qint64 size = file->size();
295 auto temp = std::make_unique<QTemporaryFile>();
296 if (!temp->open() || temp->write(file->readAll()) < 0) {
297 setInvalidMediaWithError(QMediaPlayer::ResourceError,
298 QStringLiteral(
"Cannot stage '%1'").arg(openPath));
303 const int tempFd = temp->handle();
304 const qint64 tempSize = temp->size();
305 result = OH_AVPlayer_SetFDSource(m_player, tempFd, 0, tempSize);
306 m_sourceFile = std::move(temp);
308 result = OH_AVPlayer_SetFDSource(m_player, fd, 0, size);
309 m_sourceFile = std::move(file);
312 const QByteArray url = m_media.toString(QUrl::FullyEncoded).toUtf8();
313 result = OH_AVPlayer_SetURLSource(m_player, url.constData());
316 if (result != AV_ERR_OK) {
317 qCWarning(qLcOhosMediaPlugin) <<
"Source rejected, error" <<
int(result);
318 setInvalidMediaWithError(QMediaPlayer::ResourceError,
319 QStringLiteral(
"OH_AVPlayer rejected the source"));
325 if (
auto *window = m_videoOutput->nativeWindow()) {
326 if (OH_AVPlayer_SetVideoSurface(m_player, window) == AV_ERR_OK)
327 m_videoSurfaceAttached =
true;
329 qCWarning(qLcOhosMediaPlugin) <<
"OH_AVPlayer_SetVideoSurface failed";
333 result = OH_AVPlayer_Prepare(m_player);
334 if (result != AV_ERR_OK) {
335 qCWarning(qLcOhosMediaPlugin) <<
"Prepare failed, error" <<
int(result);
336 setInvalidMediaWithError(QMediaPlayer::ResourceError,
337 QStringLiteral(
"OH_AVPlayer_Prepare failed"));
345 m_pendingPlay = !m_media.isEmpty();
349 if (m_avState == AV_PREPARED || m_avState == AV_PAUSED || m_avState == AV_COMPLETED
350 || m_avState == AV_STOPPED) {
351 m_pendingPlay =
false;
352 OH_AVPlayer_Play(m_player);
355 m_pendingPlay =
true;
361 m_pendingPlay =
false;
362 if (m_player && m_avState == AV_PLAYING)
363 OH_AVPlayer_Pause(m_player);
368 m_pendingPlay =
false;
371 if (m_avState == AV_PLAYING || m_avState == AV_PAUSED || m_avState == AV_PREPARED
372 || m_avState == AV_COMPLETED)
373 OH_AVPlayer_Stop(m_player);
378 m_audioOutput = output;
387 m_videoOutput.reset();
391 if (!m_videoOutput) {
392 m_videoOutput = std::make_unique<QOhosVideoOutput>(
393 sink, QOhosVideoOutput::ContentSource::MediaPlayer,
this);
394 connect(m_videoOutput.get(), &QOhosVideoOutput::surfaceReady,
this,
406 return m_hasVideoTrack ? 1 : 0;
408 return m_hasAudioTrack ? 1 : 0;
418 if (streamNumber != 0 || trackCount(type) == 0)
421 md.insert(QMediaMetaData::Language, QVariant::fromValue(QLocale::AnyLanguage));
429 md.insert(QMediaMetaData::Duration, m_duration);
430 if (m_videoWidth > 0 && m_videoHeight > 0)
431 md.insert(QMediaMetaData::Resolution, QSize{ m_videoWidth, m_videoHeight });
437 return trackCount(type) > 0 ? 0 : -1;
442 if (m_pendingSetMedia && !m_media.isEmpty()) {
443 m_pendingSetMedia =
false;
444 m_sourceApplied =
true;
445 applyPendingSource();
451 if (m_avState == newState)
453 m_avState = newState;
459 if (newState == AV_IDLE)
462 if (newState == AV_PREPARED) {
463 int32_t durationMs = 0;
464 if (OH_AVPlayer_GetDuration(m_player, &durationMs) == AV_ERR_OK) {
465 if (m_duration != durationMs) {
466 m_duration = durationMs;
467 durationChanged(m_duration);
470 m_seekable = m_duration > 0;
471 seekableChanged(m_seekable);
473 int32_t videoWidth = 0;
474 int32_t videoHeight = 0;
475 OH_AVPlayer_GetVideoWidth(m_player, &videoWidth);
476 OH_AVPlayer_GetVideoHeight(m_player, &videoHeight);
477 const bool hasVideo = videoWidth > 0 && videoHeight > 0;
478 if (hasVideo && m_videoOutput)
479 m_videoOutput->setVideoSize(QSize{ videoWidth, videoHeight });
480 videoAvailableChanged(hasVideo);
481 audioAvailableChanged(
true);
482 const bool prevHasVideo = m_hasVideoTrack;
483 const bool prevHasAudio = m_hasAudioTrack;
484 m_hasVideoTrack = hasVideo;
485 m_hasAudioTrack =
true;
486 if (prevHasVideo != m_hasVideoTrack || prevHasAudio != m_hasAudioTrack) {
488 activeTracksChanged();
490 m_videoWidth = videoWidth;
491 m_videoHeight = videoHeight;
495 mediaStatusChanged(mediaStatusFor(newState,
false));
496 stateChanged(playbackStateFor(newState));
498 if (newState == AV_PREPARED && m_pendingPlay) {
499 m_pendingPlay =
false;
500 OH_AVPlayer_Play(m_player);
506 mediaStatusChanged(QMediaPlayer::EndOfMedia);
507 stateChanged(QMediaPlayer::StoppedState);
521 OH_AVPlayer_GetVideoWidth(m_player, &width);
522 OH_AVPlayer_GetVideoHeight(m_player, &height);
523 m_videoWidth = width;
524 m_videoHeight = height;
525 if (m_videoOutput && width > 0 && height > 0)
526 m_videoOutput->setVideoSize(QSize{ width, height });
527 videoAvailableChanged(width > 0 && height > 0);
532 const float progress = std::clamp(bufferingPercent / 100.0f, 0.0f, 1.0f);
533 if (qFuzzyCompare(m_bufferProgress, progress))
535 m_bufferProgress = progress;
536 bufferProgressChanged(progress);
539void QOhosMediaPlayer::handleError(int32_t errorCode,
const QString &errorMsg)
541 qCWarning(qLcOhosMediaPlugin) <<
"OH_AVPlayer error" << errorCode << errorMsg;
542 setInvalidMediaWithError(QMediaPlayer::ResourceError,
543 errorMsg.isEmpty() ? QStringLiteral(
"AVPlayer error %1").arg(errorCode)
551 OH_AVPlayer_SetOnInfoCallback(m_player,
nullptr,
nullptr);
552 OH_AVPlayer_SetOnErrorCallback(m_player,
nullptr,
nullptr);
553 OH_AVPlayer_ReleaseSync(m_player);
560 m_sourceFile.reset();
561 const bool changed = m_hasVideoTrack || m_hasAudioTrack;
562 m_hasVideoTrack =
false;
563 m_hasAudioTrack =
false;
564 const bool hadMetadata = m_duration > 0 || m_videoWidth > 0 || m_videoHeight > 0;
569 activeTracksChanged();
579 m_player = OH_AVPlayer_Create();
581 qCWarning(qLcOhosMediaPlugin) <<
"OH_AVPlayer_Create failed";
584 OH_AVPlayer_SetOnInfoCallback(m_player, &QOhosMediaPlayer::onInfoTrampoline,
this);
585 OH_AVPlayer_SetOnErrorCallback(m_player, &QOhosMediaPlayer::onErrorTrampoline,
this);
596 volume = m_audioOutput->muted ? 0.0f : m_audioOutput->volume;
598 OH_AVPlayer_SetVolume(m_player, volume, volume);
601void QOhosMediaPlayer::onInfoTrampoline(OH_AVPlayer *player, AVPlayerOnInfoType type,
602 OH_AVFormat *body,
void *userData)
611 case AV_INFO_TYPE_STATE_CHANGE: {
612 AVPlayerState state{ AV_IDLE };
613 OH_AVPlayer_GetState(player, &state);
614 QMetaObject::invokeMethod(
615 self, [self, state]() { self->handleStateChange(state); }, Qt::QueuedConnection);
618 case AV_INFO_TYPE_POSITION_UPDATE: {
620 OH_AVPlayer_GetCurrentTime(player, &pos);
621 self->m_position.store(pos);
622 QMetaObject::invokeMethod(
623 self, [self, pos]() { self->positionChanged(qint64{ pos }); },
624 Qt::QueuedConnection);
627 case AV_INFO_TYPE_DURATION_UPDATE: {
629 OH_AVPlayer_GetDuration(player, &dur);
630 QMetaObject::invokeMethod(
633 if (self->m_duration == dur)
635 self->m_duration = dur;
636 self->durationChanged(qint64{ dur });
638 Qt::QueuedConnection);
641 case AV_INFO_TYPE_EOS:
642 QMetaObject::invokeMethod(
643 self, [self]() { self->handleEndOfStream(); }, Qt::QueuedConnection);
645 case AV_INFO_TYPE_SEEKDONE:
646 QMetaObject::invokeMethod(
647 self, [self]() { self->handleSeekDone(); }, Qt::QueuedConnection);
649 case AV_INFO_TYPE_RESOLUTION_CHANGE:
650 QMetaObject::invokeMethod(
651 self, [self]() { self->handleResolutionChange(); }, Qt::QueuedConnection);
658void QOhosMediaPlayer::onErrorTrampoline(OH_AVPlayer *, int32_t errorCode,
const char *errorMsg,
664 QString message = errorMsg ? QString::fromUtf8(errorMsg) : QString{};
665 QMetaObject::invokeMethod(
666 self, [self, errorCode, message]() { self->handleError(errorCode, message); },
667 Qt::QueuedConnection);
672#include "moc_qohosmediaplayer_p.cpp"
Combined button and popup list for selecting options.