205 lines
7.6 KiB
C++
205 lines
7.6 KiB
C++
|
/*********************************************************
|
||
|
* Copyright (C) 2020, Val Doroshchuk <valbok@gmail.com> *
|
||
|
* *
|
||
|
* This file is part of QtAVPlayer. *
|
||
|
* Free Qt Media Player based on FFmpeg. *
|
||
|
*********************************************************/
|
||
|
|
||
|
#include <QtAVPlayer/qavplayer.h>
|
||
|
#include <QtAVPlayer/qavvideoframe.h>
|
||
|
#include <QtAVPlayer/qavaudiooutput.h>
|
||
|
#include <QtAVPlayer/qaviodevice.h>
|
||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||
|
#include <QAbstractVideoSurface>
|
||
|
#include <private/qdeclarativevideooutput_p.h>
|
||
|
#else
|
||
|
#include <QVideoSink>
|
||
|
#include <QtMultimediaQuick/private/qquickvideooutput_p.h>
|
||
|
#endif
|
||
|
|
||
|
#include <QtQuick/QQuickView>
|
||
|
#include <QtQuick/QQuickItem>
|
||
|
#include <QtQml/QQmlEngine>
|
||
|
#include <QGuiApplication>
|
||
|
#include <QDebug>
|
||
|
#include <QElapsedTimer>
|
||
|
#include <QFile>
|
||
|
|
||
|
extern "C" {
|
||
|
#include <libavcodec/avcodec.h>
|
||
|
}
|
||
|
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||
|
class Source : public QObject
|
||
|
{
|
||
|
Q_OBJECT
|
||
|
Q_PROPERTY(QAbstractVideoSurface *videoSurface READ videoSurface WRITE setVideoSurface)
|
||
|
public:
|
||
|
explicit Source(QObject *parent = 0) : QObject(parent) { }
|
||
|
virtual ~Source() { }
|
||
|
|
||
|
QAbstractVideoSurface* videoSurface() const { return m_surface; }
|
||
|
void setVideoSurface(QAbstractVideoSurface *surface)
|
||
|
{
|
||
|
m_surface = surface;
|
||
|
}
|
||
|
|
||
|
QAbstractVideoSurface *m_surface = nullptr;
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
static bool isStreamCurrent(int index, const QList<QAVStream> &streams)
|
||
|
{
|
||
|
for (const auto &stream: streams) {
|
||
|
if (stream.index() == index)
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int main(int argc, char *argv[])
|
||
|
{
|
||
|
QGuiApplication app(argc, argv);
|
||
|
|
||
|
QQuickView viewer;
|
||
|
viewer.setSource(QUrl(QString::fromLatin1("qrc:///main.qml")));
|
||
|
viewer.setResizeMode(QQuickView::SizeRootObjectToView);
|
||
|
QObject::connect(viewer.engine(), SIGNAL(quit()), &viewer, SLOT(close()));
|
||
|
|
||
|
QQuickItem *rootObject = viewer.rootObject();
|
||
|
|
||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||
|
using VideoOutput = QDeclarativeVideoOutput;
|
||
|
#else
|
||
|
using VideoOutput = QQuickVideoOutput;
|
||
|
#endif
|
||
|
|
||
|
auto vo = rootObject->findChild<VideoOutput *>(QString::fromLatin1("videoOutput"));
|
||
|
|
||
|
QAVAudioOutput audioOutput;
|
||
|
QAVPlayer p;
|
||
|
|
||
|
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||
|
Source src;
|
||
|
vo->setSource(&src);
|
||
|
auto videoSurface = src.m_surface;
|
||
|
#elif QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||
|
auto videoSurface = vo->videoSurface();
|
||
|
// Make sure that render geometry has been updated after a frame
|
||
|
QObject::connect(vo, &QDeclarativeVideoOutput::sourceRectChanged, &p, [&] {
|
||
|
vo->update();
|
||
|
});
|
||
|
#else
|
||
|
auto videoSurface = vo->videoSink();
|
||
|
#endif
|
||
|
|
||
|
QObject::connect(&p, &QAVPlayer::videoFrame, &p, [&](const QAVVideoFrame &frame) {
|
||
|
rootObject->setProperty("frame_fps", p.progress(frame.stream()).fps());
|
||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||
|
// Might download and convert data
|
||
|
QVideoFrame videoFrame = frame;
|
||
|
if (!videoSurface->isActive())
|
||
|
videoSurface->start({videoFrame.size(), videoFrame.pixelFormat(), videoFrame.handleType()});
|
||
|
if (videoSurface->isActive())
|
||
|
videoSurface->present(videoFrame);
|
||
|
#endif
|
||
|
});
|
||
|
|
||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||
|
QObject::connect(&p, &QAVPlayer::videoFrame, &p, [&](const QAVVideoFrame &frame) {
|
||
|
// Might download and convert data
|
||
|
QVideoFrame videoFrame = frame;
|
||
|
videoSurface->setVideoFrame(videoFrame);
|
||
|
}, Qt::DirectConnection);
|
||
|
#endif
|
||
|
|
||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
|
||
|
//audioOutput.setChannelConfig(QAudioFormat::channelConfig(QAudioFormat::FrontLeft));
|
||
|
#endif
|
||
|
|
||
|
QObject::connect(&p, &QAVPlayer::audioFrame, &p, [&audioOutput](const QAVAudioFrame &frame) { audioOutput.play(frame); }, Qt::DirectConnection);
|
||
|
QString file = argc > 1 ? QString::fromUtf8(argv[1]) : QString::fromLatin1("http://archive.org/download/big-bunny-sample-video/SampleVideo.ia.mp4");
|
||
|
QString filter = argc > 2 ? QString::fromUtf8(argv[2]) : QString();
|
||
|
|
||
|
QObject::connect(&p, &QAVPlayer::stateChanged, [&](auto s) { qDebug() << "stateChanged" << s << p.mediaStatus(); });
|
||
|
QObject::connect(&p, &QAVPlayer::mediaStatusChanged, [&](auto status) {
|
||
|
qDebug() << "mediaStatusChanged"<< status << p.state();
|
||
|
if (status == QAVPlayer::LoadedMedia) {
|
||
|
auto availableVideoStreams = p.availableVideoStreams();
|
||
|
//p.setVideoStreams({});
|
||
|
auto videoStreams = p.currentVideoStreams();
|
||
|
qDebug() << "Video streams:" << availableVideoStreams.size();
|
||
|
for (auto &s : p.availableVideoStreams())
|
||
|
qDebug() << "[" << s.index() << "]" << s.metadata() << s.framesCount() << "frames," << s.frameRate() << "frame rate" << (isStreamCurrent(s.index(), videoStreams) ? "---current" : "");
|
||
|
|
||
|
auto availableAudioStreams = p.availableAudioStreams();
|
||
|
auto audioStreams = p.currentAudioStreams();
|
||
|
qDebug() << "Audio streams:" << availableAudioStreams.size();
|
||
|
|
||
|
for (auto &s : availableAudioStreams)
|
||
|
qDebug() << "[" << s.index() << "]" << s.metadata() << s.framesCount() << "frames," << s.frameRate() << "frame rate" << (isStreamCurrent(s.index(), audioStreams) ? "---current" : "");
|
||
|
|
||
|
auto availableSubtitleStreams = p.availableSubtitleStreams();
|
||
|
qDebug() << "Subtitle streams:" << availableSubtitleStreams.size();
|
||
|
for (auto &s : availableSubtitleStreams) {
|
||
|
if (s.metadata()[QString::fromLatin1("language")] == QString::fromLatin1("eng")) {
|
||
|
p.setSubtitleStream(s);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
auto subtitleStreams = p.currentSubtitleStreams();
|
||
|
for (auto &s : availableSubtitleStreams) {
|
||
|
qDebug() << "[" << s.index() << "]" << s.metadata() << s.framesCount() << "frames," << s.frameRate() << "frame rate" << (isStreamCurrent(s.index(), subtitleStreams) ? "---current" : "");
|
||
|
}
|
||
|
p.play();
|
||
|
} else if (status == QAVPlayer::EndOfMedia) {
|
||
|
for (const auto &s : p.availableVideoStreams())
|
||
|
qDebug() << s << p.progress(s);
|
||
|
for (const auto &s : p.availableAudioStreams())
|
||
|
qDebug() << s << p.progress(s);
|
||
|
for (const auto &s : p.availableSubtitleStreams())
|
||
|
qDebug() << s << p.progress(s);
|
||
|
}
|
||
|
|
||
|
});
|
||
|
QObject::connect(&p, &QAVPlayer::durationChanged, [&](auto d) { qDebug() << "durationChanged" << d; });
|
||
|
|
||
|
QObject::connect(&p, &QAVPlayer::subtitleFrame, &p, [](const QAVSubtitleFrame &frame) {
|
||
|
for (unsigned i = 0; i < frame.subtitle()->num_rects; ++i) {
|
||
|
if (frame.subtitle()->rects[i]->type == SUBTITLE_TEXT)
|
||
|
qDebug() << "text:" << frame.subtitle()->rects[i]->text;
|
||
|
else
|
||
|
qDebug() << "ass:" << frame.subtitle()->rects[i]->ass;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
QSharedPointer<QAVIODevice> qrc;
|
||
|
if (file.startsWith(QString::fromLatin1(":/"))) {
|
||
|
QSharedPointer<QIODevice> io(new QFile(file));
|
||
|
if (io->open(QIODevice::ReadOnly))
|
||
|
qrc.reset(new QAVIODevice(io));
|
||
|
}
|
||
|
p.setSource(file, qrc);
|
||
|
p.setFilter(filter);
|
||
|
//p.setSynced(false);
|
||
|
|
||
|
viewer.setMinimumSize(QSize(300, 360));
|
||
|
viewer.resize(1960, 1086);
|
||
|
viewer.show();
|
||
|
|
||
|
QElapsedTimer qmlElapsed;
|
||
|
qmlElapsed.start();
|
||
|
int qmlCount = 0;
|
||
|
|
||
|
QObject::connect(&viewer, &QQuickView::afterRendering, &viewer, [&] {
|
||
|
const int fps = qmlCount++ * 1000 / qmlElapsed.elapsed();
|
||
|
rootObject->setProperty("qml_fps", fps);
|
||
|
});
|
||
|
|
||
|
return app.exec();
|
||
|
}
|
||
|
|
||
|
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||
|
#include "main.moc"
|
||
|
#endif
|