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,
405 case TrackType::VideoStream:
406 return m_hasVideoTrack ? 1 : 0;
407 case TrackType::AudioStream:
408 return m_hasAudioTrack ? 1 : 0;
409 case TrackType::SubtitleStream:
417 if (streamNumber != 0 || trackCount(type) == 0)
420 md.insert(QMediaMetaData::Language, QVariant::fromValue(QLocale::AnyLanguage));
428 md.insert(QMediaMetaData::Duration, m_duration);
429 if (m_videoWidth > 0 && m_videoHeight > 0)
430 md.insert(QMediaMetaData::Resolution, QSize{ m_videoWidth, m_videoHeight });
436 return trackCount(type) > 0 ? 0 : -1;
441 if (m_pendingSetMedia && !m_media.isEmpty()) {
442 m_pendingSetMedia =
false;
443 m_sourceApplied =
true;
444 applyPendingSource();
450 if (m_avState == newState)
452 m_avState = newState;
458 if (newState == AV_IDLE)
461 if (newState == AV_PREPARED) {
462 int32_t durationMs = 0;
463 if (OH_AVPlayer_GetDuration(m_player, &durationMs) == AV_ERR_OK) {
464 if (m_duration != durationMs) {
465 m_duration = durationMs;
466 durationChanged(m_duration);
469 m_seekable = m_duration > 0;
470 seekableChanged(m_seekable);
472 int32_t videoWidth = 0;
473 int32_t videoHeight = 0;
474 OH_AVPlayer_GetVideoWidth(m_player, &videoWidth);
475 OH_AVPlayer_GetVideoHeight(m_player, &videoHeight);
476 const bool hasVideo = videoWidth > 0 && videoHeight > 0;
477 if (hasVideo && m_videoOutput)
478 m_videoOutput->setVideoSize(QSize{ videoWidth, videoHeight });
479 videoAvailableChanged(hasVideo);
480 audioAvailableChanged(
true);
481 const bool prevHasVideo = m_hasVideoTrack;
482 const bool prevHasAudio = m_hasAudioTrack;
483 m_hasVideoTrack = hasVideo;
484 m_hasAudioTrack =
true;
485 if (prevHasVideo != m_hasVideoTrack || prevHasAudio != m_hasAudioTrack) {
487 activeTracksChanged();
489 m_videoWidth = videoWidth;
490 m_videoHeight = videoHeight;
494 mediaStatusChanged(mediaStatusFor(newState,
false));
495 stateChanged(playbackStateFor(newState));
497 if (newState == AV_PREPARED && m_pendingPlay) {
498 m_pendingPlay =
false;
499 OH_AVPlayer_Play(m_player);
505 mediaStatusChanged(QMediaPlayer::EndOfMedia);
506 stateChanged(QMediaPlayer::StoppedState);
520 OH_AVPlayer_GetVideoWidth(m_player, &width);
521 OH_AVPlayer_GetVideoHeight(m_player, &height);
522 m_videoWidth = width;
523 m_videoHeight = height;
524 if (m_videoOutput && width > 0 && height > 0)
525 m_videoOutput->setVideoSize(QSize{ width, height });
526 videoAvailableChanged(width > 0 && height > 0);
531 const float progress = std::clamp(bufferingPercent / 100.0f, 0.0f, 1.0f);
532 if (qFuzzyCompare(m_bufferProgress, progress))
534 m_bufferProgress = progress;
535 bufferProgressChanged(progress);
538void QOhosMediaPlayer::handleError(int32_t errorCode,
const QString &errorMsg)
540 qCWarning(qLcOhosMediaPlugin) <<
"OH_AVPlayer error" << errorCode << errorMsg;
541 setInvalidMediaWithError(QMediaPlayer::ResourceError,
542 errorMsg.isEmpty() ? QStringLiteral(
"AVPlayer error %1").arg(errorCode)
550 OH_AVPlayer_SetOnInfoCallback(m_player,
nullptr,
nullptr);
551 OH_AVPlayer_SetOnErrorCallback(m_player,
nullptr,
nullptr);
552 OH_AVPlayer_ReleaseSync(m_player);
559 m_sourceFile.reset();
560 const bool changed = m_hasVideoTrack || m_hasAudioTrack;
561 m_hasVideoTrack =
false;
562 m_hasAudioTrack =
false;
563 const bool hadMetadata = m_duration > 0 || m_videoWidth > 0 || m_videoHeight > 0;
568 activeTracksChanged();
578 m_player = OH_AVPlayer_Create();
580 qCWarning(qLcOhosMediaPlugin) <<
"OH_AVPlayer_Create failed";
583 OH_AVPlayer_SetOnInfoCallback(m_player, &QOhosMediaPlayer::onInfoTrampoline,
this);
584 OH_AVPlayer_SetOnErrorCallback(m_player, &QOhosMediaPlayer::onErrorTrampoline,
this);
595 volume = m_audioOutput->muted ? 0.0f : m_audioOutput->volume;
597 OH_AVPlayer_SetVolume(m_player, volume, volume);
600void QOhosMediaPlayer::onInfoTrampoline(OH_AVPlayer *player, AVPlayerOnInfoType type,
601 OH_AVFormat *body,
void *userData)
610 case AV_INFO_TYPE_STATE_CHANGE: {
611 AVPlayerState state{ AV_IDLE };
612 OH_AVPlayer_GetState(player, &state);
613 QMetaObject::invokeMethod(
614 self, [self, state]() { self->handleStateChange(state); }, Qt::QueuedConnection);
617 case AV_INFO_TYPE_POSITION_UPDATE: {
619 OH_AVPlayer_GetCurrentTime(player, &pos);
620 self->m_position.store(pos);
621 QMetaObject::invokeMethod(
622 self, [self, pos]() { self->positionChanged(qint64{ pos }); },
623 Qt::QueuedConnection);
626 case AV_INFO_TYPE_DURATION_UPDATE: {
628 OH_AVPlayer_GetDuration(player, &dur);
629 QMetaObject::invokeMethod(
632 if (self->m_duration == dur)
634 self->m_duration = dur;
635 self->durationChanged(qint64{ dur });
637 Qt::QueuedConnection);
640 case AV_INFO_TYPE_EOS:
641 QMetaObject::invokeMethod(
642 self, [self]() { self->handleEndOfStream(); }, Qt::QueuedConnection);
644 case AV_INFO_TYPE_SEEKDONE:
645 QMetaObject::invokeMethod(
646 self, [self]() { self->handleSeekDone(); }, Qt::QueuedConnection);
648 case AV_INFO_TYPE_RESOLUTION_CHANGE:
649 QMetaObject::invokeMethod(
650 self, [self]() { self->handleResolutionChange(); }, Qt::QueuedConnection);
657void QOhosMediaPlayer::onErrorTrampoline(OH_AVPlayer *, int32_t errorCode,
const char *errorMsg,
663 QString message = errorMsg ? QString::fromUtf8(errorMsg) : QString{};
664 QMetaObject::invokeMethod(
665 self, [self, errorCode, message]() { self->handleError(errorCode, message); },
666 Qt::QueuedConnection);
671#include "moc_qohosmediaplayer_p.cpp"
Combined button and popup list for selecting options.