Qt 6 QML MediaPlayer not playing M3U8 streams on Android, while WebView does
-
Hello!
I'm trying to stream an IP camera, but the application output window first displays a warning:
W/qt.multimedia.ffmpeg.mediadataholder(14712): Failed to open media file. FFmpeg error description: Invalid data encountered while processing input.Then, in onErrorOccurred, I get the error:
E/qml(14712): MediaPlayer Error: 2 Failed to open file.
Here's my code:... MediaPlayer { id: mediaPlayer source: "https://cdn.some_domen/.../stream.m3u8?uuid=some_uuid&token=some_token" videoOutput: videoOutput audioOutput: AudioOutput {} onErrorOccurred: { console.error("MediaPlayer error:", mediaPlayer.error, mediaPlayer.errorString); } } VideoOutput { id: videoOutput anchors.fill: parent fillMode: VideoOutput.PreserveAspectFit MouseArea { anchors.fill: parent onClicked: { if (mediaPlayer.playbackState === MediaPlayer.PlayingState) mediaPlayer.pause(); else mediaPlayer.play(); } } } ...In the C++ code, I check for OpenSSL:
qDebug() << "OpenSSL supported:" << QSslSocket::supportsSsl(); qDebug() << QSslSocket::supportsSsl() << QSslSocket::sslLibraryBuildVersionString() <<" , "<< QSslSocket::sslLibraryVersionString(); qDebug() << manager->supportedSchemes();In the application output window, I see:
D/default (14712): OpenSSL supported: true
D/default (14712): true "OpenSSL 3.0.7 1 Nov 2022", "OpenSSL 3.1.8 11 Feb 2025"
D/default (14712): QList("file", "qrc", "assets", "http", "unix+http", "local+http", "https", "data")
.
However, if I use WebView instead of MediaPlayer, like this:... WebView { id: webview ... url: "https://cdn.some_domen/.../stream.m3u8?uuid=some_uuid&token=some_token" ... } ..., then stream playback is working.
The Qt version is 6.10.1.
Has anyone come across this?
Thanks in advance! -
I tried using the libvlc library, c++:
#include <vlc/vlc.h> #include "vlc/libvlc_media.h" #include "vlc/libvlc_media_player.h" #include <QQuickItem> class VLCPlayer : public QQuickItem { Q_OBJECT QML_ELEMENT // This makes the class available for use/instantiation on the QML side. Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged) public: VLCPlayer() { qDebug() << "libvlc_new(0, nullptr)..."; _vlcInstance = libvlc_new(0, nullptr); qDebug() << "libvlc_media_player_new..."; _vlcPlayer = libvlc_media_player_new(_vlcInstance); } void setSource(const QString &path) { libvlc_media_t *m = libvlc_media_new_path(_vlcInstance, path.toUtf8().data()); libvlc_media_player_set_media(_vlcPlayer, m); libvlc_media_release(m); libvlc_media_player_play(_vlcPlayer); emit sourceChanged(); } QString source() const { return ""; } signals: void sourceChanged(); private: libvlc_instance_t *_vlcInstance; libvlc_media_player_t *_vlcPlayer; };and QML:
VLCPlayer { id: player anchors.fill: parent source: "https://cdn.some_domen/.../stream.m3u8?uuid=some_uuid&token=some_token" },
but in the constructor, on the line
_vlcInstance = libvlc_new(0, nullptr);
the program crashes with the error:
Fatal signal 6 (SIGABRT), code -6 (SI_TKILL) in tid 10680 (qtMainLoopThrea)... . -
After a long conversation with the AI, I managed to get playback working using the libvlc library!
-
If anyone is interested, here is the code:
main.cpp#include <QGuiApplication> #include <QQmlApplicationEngine> int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); // 1. Load dependencies in the correct order via the Java layer // This will automatically call JNI_OnLoad and install s_jvm in VLC QJniObject::callStaticMethod<void>( "java/lang/System", "loadLibrary", "(Ljava/lang/String;)V", QJniObject::fromString("vlc").object() // core first ); QQmlApplicationEngine engine; QObject::connect( &engine, &QQmlApplicationEngine::objectCreationFailed, &app, []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.loadFromModule("myproject", "Main"); return app.exec(); }.
-
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?