Qt 6 QML MediaPlayer not playing M3U8 streams on Android, while WebView does
-
vlcplayer.h
#ifndef VLCPLAYER_H #define VLCPLAYER_H #include <vlc/vlc.h> #include "vlc/libvlc_media.h" #include "vlc/libvlc_media_player.h" #include <QtQml/qqmlregistration.h> //for QML_ELEMENT #include <QMutex> #include <vector> #include <QPointer> #include <QVideoSink> // using namespace QNativeInterface; // extern "C" { // // We declare the function we found using nm // void libvlc_media_player_set_android_context(libvlc_media_player_t *p_mi, void *p_jvm_or_surface); // } class VLCPlayer : public QObject { Q_OBJECT QML_ELEMENT // This makes the class available for use/instantiation on the QML side. Q_PROPERTY(QVideoSink* videoSink READ videoSink WRITE setVideoSink NOTIFY videoSinkChanged) public: VLCPlayer(); Q_INVOKABLE void setSource(const QString &path); // 1. LOCK: VLC asks where to write the pixels of the next frame static void* lock(void *opaque, void **planes); // 2. UNLOCK: The frame has been written to the buffer static void unlock(void *opaque, void *picture, void *const *planes); // 3. DISPLAY: It's time to tell Qt that the image has been updated static void display(void *opaque, void *picture); QVideoSink *videoSink() const; void setVideoSink(QVideoSink *newVideoSink); // Video settings (can be changed) const int VIDEO_WIDTH = 1280; const int VIDEO_HEIGHT = 720; std::vector<uchar> videoBuffer; QMutex bufferMutex; libvlc_media_player_t *mp = nullptr; signals: void videoSinkChanged(); void frameReady(); public slots: void onframeReady(); private: libvlc_instance_t *_vlcInstance; libvlc_media_player_t *_vlcPlayer; QPointer<QVideoSink> m_videoSink; }; -
vlcplayer.cpp
-
I can't paste it - I get a message that it's spam...
-
This post is deleted!
-
This post is deleted!
-
This post is deleted!
-
#include "vlcplayer.h" #include <QPainter> #include <QVideoFrame> VLCPlayer::VLCPlayer() { videoBuffer.resize(VIDEO_WIDTH * VIDEO_HEIGHT * 4); // RGBA connect(this, &VLCPlayer::frameReady, this, &VLCPlayer::onframeReady); } void VLCPlayer::setSource(const QString &path) { // const char* vlc_args[] = { "--no-osd" }; _vlcInstance = libvlc_new(0, nullptr); //libvlc_new(1, vlc_args); _vlcPlayer = libvlc_media_player_new(_vlcInstance); // Set the format: RGBA (4 bytes per pixel), width, height, pitch libvlc_video_set_format(_vlcPlayer, "RGBA", VIDEO_WIDTH, VIDEO_HEIGHT, VIDEO_WIDTH * 4); // Link our functions libvlc_video_set_callbacks(_vlcPlayer, &VLCPlayer::lock, &VLCPlayer::unlock, &VLCPlayer::display, this); libvlc_media_t *m = libvlc_media_new_location(_vlcInstance, path.toUtf8().constData()); libvlc_media_player_set_media(_vlcPlayer, m); libvlc_media_release(m); libvlc_media_player_play(_vlcPlayer); } QVideoSink* VLCPlayer::videoSink() const { return m_videoSink.get(); } void VLCPlayer::setVideoSink(QVideoSink *newVideoSink) { if (m_videoSink == newVideoSink) return; m_videoSink = newVideoSink; emit videoSinkChanged(); } void* VLCPlayer::lock(void *opaque, void **planes) { VLCPlayer *player = static_cast<VLCPlayer*>(opaque); player->bufferMutex.lock(); *planes = player->videoBuffer.data(); return nullptr; } void VLCPlayer::unlock(void *opaque, void *picture, void *const *planes) { VLCPlayer *player = static_cast<VLCPlayer*>(opaque); player->bufferMutex.unlock(); } void VLCPlayer::display(void *opaque, void *picture) { VLCPlayer *player = static_cast<VLCPlayer*>(opaque); // Generate a signal to redraw the user interface in Qt's main thread emit player->frameReady(); } void VLCPlayer::onframeReady() { if(!m_videoSink) return; bufferMutex.lock(); // Create a QImage object that simply "looks" at your video buffer (without copying) QImage img(videoBuffer.data(), VIDEO_WIDTH, VIDEO_HEIGHT, QImage::Format_RGBA8888); // In Qt 6, you can create a frame directly from a QImage // if the formats match, Qt will try to minimize overhead QVideoFrame frame(img); // Sending to Sink m_videoSink->setVideoFrame(frame); bufferMutex.unlock(); } -
Because of the phrase "#include <Q D e b u g>," Askit considered this code spam.)
However, QDebug still needs to be enabled. -
QML
VLCPlayer { id: vlcplayer videoSink: videoOutput.videoSink } VideoOutput { id: videoOutput anchors.fill: parent fillMode: VideoOutput.PreserveAspectFit } Component.onCompleted: vlcplayer.setSource("https://cdn.some_domen/.../stream.m3u8?uuid=some_uuid&token=some_token") -
Interesting! I recently did some experimentation with Qt combined with IP camera, and I had less-than-ideal results (which I planned to revisit at some point but have not gotten around to).
@woodpecker How is the timing/lag/performance?
My principal issue was huge lag, which I also experienced when just using ffmpeg at the command line. (Since Qt uses ffmpeg under the hood I suspect the actual source of lag is therefore ffmpeg.)
I had come across:
https://forum.qt.io/topic/79208/how-to-kill-delay-in-live-streaming (which never got any reply at all)
https://stackoverflow.com/questions/7759695/terrible-performance-with-qmediaplayer-and-qvideowidget
But your (very) recent outcome will be of interest to myself and others! How's the lag?
(Also, you mentioned 6.10.1 but can you also state which OS or OSes?) EDIT: Android is in the title! But still I'm curious if you build for Linux/Windows also?