diff --git a/.gitignore b/.gitignore index 8a9d35c887947b2618399e6cd64808e7df019e67..996ee8959f07b650ea45516a96e71080b7a8d204 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.user +build* diff --git a/.idea/misc.xml b/.idea/misc.xml index cc6c7696d5eb7b96c5ce365fa4bae2d7a8f66da5..30681992d74a91f527acc5761a4f61338e84fb71 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,11 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" /> + <component name="CidrRootsConfiguration"> + <excludeRoots> + <file path="$PROJECT_DIR$/build-qt59" /> + </excludeRoots> + </component> <component name="ProjectPlainTextFileTypeManager"> <file url="file://$PROJECT_DIR$/AVQt/mainpage.dox" /> </component> diff --git a/AVQt/AVQt b/AVQt/AVQt index c554c7d4ce0ce513832a76995db98a626ba97981..876471bc02b7109fa473e60c7142796d7bf5acf8 100644 --- a/AVQt/AVQt +++ b/AVQt/AVQt @@ -6,11 +6,13 @@ #include "filter/DecoderVAAPI.h" #include "filter/DecoderQSV.h" #include "filter/DecoderDXVA2.h" +#include "filter/DecoderMMAL.h" #include "filter/AudioDecoder.h" +#include "filter/EncoderVAAPI.h" #include "output/IPacketSink.h" #include "output/IFrameSink.h" #include "output/IAudioSink.h" #include "output/OpenGLRenderer.h" #include "output/OpenALAudioOutput.h" -#include "filter/EncoderVAAPI.h" -//#include "output/FrameFileSaver.h" \ No newline at end of file +#include "output/Muxer.h" +//#include "output/FrameFileSaver.h" diff --git a/AVQt/CMakeLists.txt b/AVQt/CMakeLists.txt index 2d92e7ac4d5e2bf2dd1dbdb869e17b52f9dbbdda..4584f5d4d87f7aa088ef2044aa14346534f7e2a7 100644 --- a/AVQt/CMakeLists.txt +++ b/AVQt/CMakeLists.txt @@ -9,8 +9,8 @@ set(CMAKE_AUTOUIC on) set(CMAKE_INCLUDE_CURRENT_DIR on) -find_package(Qt6 COMPONENTS Core Gui Concurrent Widgets OpenGL OpenGLWidgets) -#find_package(Qt5 COMPONENTS Core Gui Concurrent Widgets OpenGL) +#find_package(Qt6 COMPONENTS Core Gui Concurrent Widgets OpenGL OpenGLWidgets) +find_package(Qt5 COMPONENTS Core Gui Concurrent Widgets OpenGL) if (WIN32) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS on) @@ -50,6 +50,10 @@ set(SOURCES filter/private/DecoderDXVA2_p.h filter/DecoderDXVA2.cpp + filter/DecoderMMAL.h + filter/private/DecoderMMAL_p.h + filter/DecoderMMAL.cpp + filter/AudioDecoder.h filter/private/AudioDecoder_p.h filter/private/OpenALErrorHandler.h @@ -63,6 +67,10 @@ set(SOURCES filter/private/EncoderVAAPI_p.h filter/EncoderVAAPI.cpp + output/Muxer.h + output/private/Muxer_p.h + output/Muxer.cpp + output/RenderClock.h output/private/RenderClock_p.h output/RenderClock.cpp @@ -85,6 +93,7 @@ target_compile_options(AVQt PRIVATE -Werror=all -Werror=extra -Werror=pedantic + -Wno-float-equal -ansi -Werror=init-self -Werror=old-style-cast @@ -167,6 +176,7 @@ target_compile_options(AVQtStatic PRIVATE -Wdouble-promotion ) -target_link_libraries(AVQt Qt6::Core Qt6::Gui Qt6::Concurrent Qt6::Widgets Qt6::OpenGL Qt6::OpenGLWidgets avformat avfilter avutil avcodec avdevice swscale swresample GL openal) -target_link_libraries(AVQtStatic Qt6::Core Qt6::Gui Qt6::Concurrent Qt6::Widgets Qt6::OpenGL Qt6::OpenGLWidgets avformat avfilter avutil avcodec avdevice swscale swresample GL openal) -#target_link_libraries(AVQt Qt5::Core Qt5::Gui Qt5::Concurrent Qt5::Widgets Qt5::OpenGL avformat avfilter avutil avcodec avdevice swscale swresample GL openal) \ No newline at end of file +#target_link_libraries(AVQt Qt6::Core Qt6::Gui Qt6::Concurrent Qt6::Widgets Qt6::OpenGL Qt6::OpenGLWidgets avformat avfilter avutil avcodec avdevice swscale swresample GL openal) +#target_link_libraries(AVQtStatic Qt6::Core Qt6::Gui Qt6::Concurrent Qt6::Widgets Qt6::OpenGL Qt6::OpenGLWidgets avformat avfilter avutil avcodec avdevice swscale swresample GL openal) +target_link_libraries(AVQt Qt5::Core Qt5::Gui Qt5::Concurrent Qt5::Widgets Qt5::OpenGL avformat avfilter avutil avcodec avdevice swscale swresample GL openal) +target_link_libraries(AVQtStatic Qt5::Core Qt5::Gui Qt5::Concurrent Qt5::Widgets Qt5::OpenGL avformat avfilter avutil avcodec avdevice swscale swresample GL openal) \ No newline at end of file diff --git a/AVQt/filter/AudioDecoder.cpp b/AVQt/filter/AudioDecoder.cpp index 88dbccb5171de9d27f330b56f81482340951314a..867c7d337c0aaf2f73b8fae4345fbd88ba322808 100644 --- a/AVQt/filter/AudioDecoder.cpp +++ b/AVQt/filter/AudioDecoder.cpp @@ -10,22 +10,21 @@ #include <QtCore> -#include <AL/al.h> -#include <AL/alc.h> #include <AL/alext.h> namespace AVQt { AudioDecoder::AudioDecoder(QObject *parent) : QThread(parent), d_ptr(new AudioDecoderPrivate(this)) { - Q_D(AVQt::AudioDecoder); } [[maybe_unused]] AudioDecoder::AudioDecoder(AVQt::AudioDecoderPrivate &p) : d_ptr(&p) { - Q_D(AVQt::AudioDecoder); } - AudioDecoder::~AudioDecoder() { - deinit(); + AudioDecoder::AudioDecoder(AudioDecoder &&other) noexcept: d_ptr(other.d_ptr) { + d_ptr->q_ptr = this; + other.d_ptr = nullptr; + } + AudioDecoder::~AudioDecoder() { delete d_ptr; d_ptr = nullptr; } @@ -128,7 +127,7 @@ namespace AVQt { return d->m_paused.load(); } - int AudioDecoder::registerCallback(IAudioSink *callback) { + qint64 AudioDecoder::registerCallback(IAudioSink *callback) { Q_D(AVQt::AudioDecoder); QMutexLocker lock(&d->m_cbListMutex); @@ -142,12 +141,13 @@ namespace AVQt { if (d->m_running) { callback->start(this); } + return d->m_cbList.indexOf(callback); } - return 0; + return -1; } - int AudioDecoder::unregisterCallback(IAudioSink *callback) { + qint64 AudioDecoder::unregisterCallback(IAudioSink *callback) { Q_D(AVQt::AudioDecoder); QMutexLocker lock(&d->m_cbListMutex); @@ -161,10 +161,10 @@ namespace AVQt { void AudioDecoder::init(IPacketSource *source, AVRational framerate, AVRational timebase, int64_t duration, AVCodecParameters *vParams, AVCodecParameters *aParams, AVCodecParameters *sParams) { Q_D(AVQt::AudioDecoder); - Q_UNUSED(source); - Q_UNUSED(framerate); - Q_UNUSED(vParams); - Q_UNUSED(sParams); + Q_UNUSED(source) + Q_UNUSED(framerate) + Q_UNUSED(vParams) + Q_UNUSED(sParams) d->m_timebase = timebase; d->m_duration = duration; @@ -183,29 +183,26 @@ namespace AVQt { } void AudioDecoder::deinit(IPacketSource *source) { - Q_D(AVQt::AudioDecoder); Q_UNUSED(source) deinit(); } void AudioDecoder::start(IPacketSource *source) { - Q_D(AVQt::AudioDecoder); - Q_UNUSED(source); + Q_UNUSED(source) start(); } void AudioDecoder::stop(IPacketSource *source) { - Q_D(AVQt::AudioDecoder); - Q_UNUSED(source); + Q_UNUSED(source) stop(); } void AudioDecoder::onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) { Q_D(AVQt::AudioDecoder); - Q_UNUSED(source); + Q_UNUSED(source) if (packetType == IPacketSource::CB_AUDIO) { AVPacket *queuePacket = av_packet_clone(packet); @@ -223,9 +220,9 @@ namespace AVQt { while (d->m_running.load()) { if (!d->m_paused.load() && !d->m_inputQueue.isEmpty()) { QTime time1 = QTime::currentTime(); - qDebug("Audio packet queue size: %lld", d->m_inputQueue.size()); + qDebug("Audio packet queue size: %d", d->m_inputQueue.size()); - int ret = 0; + int ret; constexpr size_t strBufSize = 64; char strBuf[strBufSize]; @@ -258,7 +255,7 @@ namespace AVQt { AVPacket *packet = d->m_inputQueue.dequeue(); lock.unlock(); - qDebug("Audio packet queue size: %lld", d->m_inputQueue.size()); + qDebug("Audio packet queue size: %d", d->m_inputQueue.size()); ret = avcodec_send_packet(d->m_pCodecCtx, packet); if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) { @@ -288,15 +285,17 @@ namespace AVQt { AVFrame *cbFrame = av_frame_clone(frame); cbFrame->pts = av_rescale_q(frame->pts, d->m_timebase, av_make_q(1, 1000000)); // Rescale pts to microseconds for easier processing - qDebug("Calling audio frame callback for PTS: %ld, Timebase: %d/%d", cbFrame->pts, d->m_timebase.num, + qDebug("Calling audio frame callback for PTS: %lld, Timebase: %d/%d", static_cast<long long>(cbFrame->pts), + d->m_timebase.num, d->m_timebase.den); QTime time = QTime::currentTime(); - cb->onAudioFrame(this, cbFrame, frame->nb_samples * 1.0 / frame->sample_rate / frame->channels * 1000.0); + cb->onAudioFrame(this, cbFrame, + static_cast<uint32_t>(frame->nb_samples * 1.0 / frame->sample_rate / frame->channels * 1000.0)); qDebug() << "Audio CB time:" << time.msecsTo(QTime::currentTime()); av_frame_unref(cbFrame); } int64_t sleepDuration = (frame->nb_samples / frame->sample_rate * 1000) - time1.msecsTo(QTime::currentTime()); - msleep(sleepDuration <= 0 ? 0 : sleepDuration); + msleep(sleepDuration <= 0 ? 0 : static_cast<unsigned long>(sleepDuration)); time1 = QTime::currentTime(); av_frame_unref(frame); } diff --git a/AVQt/filter/AudioDecoder.h b/AVQt/filter/AudioDecoder.h index 44487311c0bcdb733dc03bada30031afc37f3e0a..d0dba7fdc3fa2e281019d5781b8b38a2ec451a10 100644 --- a/AVQt/filter/AudioDecoder.h +++ b/AVQt/filter/AudioDecoder.h @@ -24,13 +24,19 @@ namespace AVQt { public: explicit AudioDecoder(QObject *parent = nullptr); + AudioDecoder(AudioDecoder &&other) noexcept; + + AudioDecoder(const AudioDecoder &) = delete; + + void operator=(const AudioDecoder &) = delete; + ~AudioDecoder() Q_DECL_OVERRIDE; bool isPaused() Q_DECL_OVERRIDE; - int registerCallback(IAudioSink *callback) Q_DECL_OVERRIDE; + qint64 registerCallback(IAudioSink *callback) Q_DECL_OVERRIDE; - int unregisterCallback(IAudioSink *callback) Q_DECL_OVERRIDE; + qint64 unregisterCallback(IAudioSink *callback) Q_DECL_OVERRIDE; void run() Q_DECL_OVERRIDE; @@ -73,4 +79,4 @@ namespace AVQt { }; } -#endif //LIBAVQT_AUDIODECODER_H +#endif //LIBAVQT_AUDIODECODER_H \ No newline at end of file diff --git a/AVQt/filter/DecoderDXVA2.cpp b/AVQt/filter/DecoderDXVA2.cpp index 3362f7a34e45aba3775aae6a5fcbdc2f6c3027c3..41c5c294f37b2a6558dab609c3b126fea351c6d1 100644 --- a/AVQt/filter/DecoderDXVA2.cpp +++ b/AVQt/filter/DecoderDXVA2.cpp @@ -9,27 +9,29 @@ #include "input/IPacketSource.h" #include <QApplication> -#include <QtConcurrent> #include <QImage> +//#include <QtConcurrent> -#ifndef DOXYGEN_SHOULD_SKIP_THIS -#define NOW() std::chrono::high_resolution_clock::now() -#define TIME_US(t1, t2) std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count() -#endif +//#ifndef DOXYGEN_SHOULD_SKIP_THIS +//#define NOW() std::chrono::high_resolution_clock::now() +//#define TIME_US(t1, t2) std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count() +//#endif namespace AVQt { DecoderDXVA2::DecoderDXVA2(QObject *parent) : QThread(parent), d_ptr(new DecoderDXVA2Private(this)) { - Q_D(AVQt::DecoderDXVA2); } [[maybe_unused]] DecoderDXVA2::DecoderDXVA2(DecoderDXVA2Private &p) : d_ptr(&p) { } - DecoderDXVA2::~DecoderDXVA2() { - deinit(); + DecoderDXVA2::DecoderDXVA2(DecoderDXVA2 &&other) noexcept: d_ptr(other.d_ptr) { + other.d_ptr = nullptr; + d_ptr->q_ptr = this; + } + DecoderDXVA2::~DecoderDXVA2() { delete d_ptr; } @@ -57,8 +59,6 @@ namespace AVQt { } int DecoderDXVA2::init() { - Q_D(AVQt::DecoderDXVA2); - return 0; } @@ -158,7 +158,7 @@ namespace AVQt { return d->m_paused.load(); } - int DecoderDXVA2::registerCallback(IFrameSink *frameSink) { + qint64 DecoderDXVA2::registerCallback(IFrameSink *frameSink) { Q_D(AVQt::DecoderDXVA2); QMutexLocker lock(&d->m_cbListMutex); @@ -173,11 +173,11 @@ namespace AVQt { return -1; } - int DecoderDXVA2::unregisterCallback(IFrameSink *frameSink) { + qint64 DecoderDXVA2::unregisterCallback(IFrameSink *frameSink) { Q_D(AVQt::DecoderDXVA2); QMutexLocker lock(&d->m_cbListMutex); if (d->m_cbList.contains(frameSink)) { - int result = d->m_cbList.indexOf(frameSink); + auto result = d->m_cbList.indexOf(frameSink); d->m_cbList.removeOne(frameSink); frameSink->stop(this); frameSink->deinit(this); @@ -187,7 +187,7 @@ namespace AVQt { } void DecoderDXVA2::onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) { - Q_UNUSED(source); + Q_UNUSED(source) Q_D(AVQt::DecoderDXVA2); @@ -244,7 +244,7 @@ namespace AVQt { AVPacket *packet = d->m_inputQueue.dequeue(); lock.unlock(); - qDebug("Video packet queue size: %lld", d->m_inputQueue.size()); + qDebug("Video packet queue size: %d", d->m_inputQueue.size()); ret = avcodec_send_packet(d->m_pCodecCtx, packet); if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) { @@ -283,7 +283,8 @@ namespace AVQt { AVFrame *cbFrame = av_frame_clone(frame); cbFrame->pts = av_rescale_q(frame->pts, d->m_timebase, av_make_q(1, 1000000)); // Rescale pts to microseconds for easier processing - qDebug("Calling video frame callback for PTS: %ld, Timebase: %d/%d", cbFrame->pts, d->m_timebase.num, + qDebug("Calling video frame callback for PTS: %lld, Timebase: %d/%d", static_cast<long long>(cbFrame->pts), + d->m_timebase.num, d->m_timebase.den); QTime time = QTime::currentTime(); cb->onFrame(this, cbFrame, static_cast<int64_t>(av_q2d(av_inv_q(d->m_framerate)) * 1000.0)); diff --git a/AVQt/filter/DecoderDXVA2.h b/AVQt/filter/DecoderDXVA2.h index 9079a432095def79cb3ade21224bbdf3d5aa4582..7c4eee29f656c4831c24ce98af6a9609b5632a9f 100644 --- a/AVQt/filter/DecoderDXVA2.h +++ b/AVQt/filter/DecoderDXVA2.h @@ -35,10 +35,10 @@ namespace AVQt { * that either at least one callback is registered or the decoder is paused, or frames will be dropped */ class DecoderDXVA2 : public QThread, public IDecoder { - Q_OBJECT - Q_INTERFACES(AVQt::IDecoder) - // Q_INTERFACES(AVQt::IFrameSource) - // Q_INTERFACES(AVQt::IPacketSink) + Q_OBJECT + Q_INTERFACES(AVQt::IDecoder) + // Q_INTERFACES(AVQt::IFrameSource) + // Q_INTERFACES(AVQt::IPacketSink) Q_DECLARE_PRIVATE(AVQt::DecoderDXVA2) @@ -52,6 +52,18 @@ namespace AVQt { */ explicit DecoderDXVA2(QObject *parent = nullptr); + DecoderDXVA2(DecoderDXVA2 &&other) noexcept; + + /*! + * \private + */ + DecoderDXVA2(const DecoderDXVA2 &) = delete; + + /*! + * \private + */ + void operator=(const DecoderDXVA2 &) = delete; + /*! * \private */ @@ -69,14 +81,14 @@ namespace AVQt { * @param type One element or some bitwise or combination of elements of IFrameSource::CB_TYPE * @return Current position in callback list */ - Q_INVOKABLE int registerCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE; + Q_INVOKABLE qint64 registerCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE; /*! * \brief Removes frame sink/filter from registry * @param frameSink Frame sink/filter to be removed * @return Last position in callback list, -1 when not found */ - Q_INVOKABLE int unregisterCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE; + Q_INVOKABLE qint64 unregisterCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE; public slots: /*! @@ -160,4 +172,4 @@ namespace AVQt { } -#endif //TRANSCODE_DECODERDXVA2_H +#endif //TRANSCODE_DECODERDXVA2_H \ No newline at end of file diff --git a/AVQt/filter/DecoderMMAL.cpp b/AVQt/filter/DecoderMMAL.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2ddf4d6df1a95f9e61e51386d57ae98897e5a936 --- /dev/null +++ b/AVQt/filter/DecoderMMAL.cpp @@ -0,0 +1,324 @@ +// +// Created by silas on 3/1/21. +// + +#include "private/DecoderMMAL_p.h" +#include "DecoderMMAL.h" +#include "output/IFrameSink.h" +#include "output/IAudioSink.h" +#include "input/IPacketSource.h" + +#include <QApplication> +#include <QImage> +//#include <QtConcurrent> + +//#ifndef DOXYGEN_SHOULD_SKIP_THIS +//#define NOW() std::chrono::high_resolution_clock::now() +//#define TIME_US(t1, t2) std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count() +//#endif + + +namespace AVQt { + DecoderMMAL::DecoderMMAL(QObject *parent) : QThread(parent), d_ptr(new DecoderMMALPrivate(this)) { + } + + [[maybe_unused]] DecoderMMAL::DecoderMMAL(DecoderMMALPrivate &p) : d_ptr(&p) { + + } + + DecoderMMAL::DecoderMMAL(DecoderMMAL &&other) noexcept: d_ptr(other.d_ptr) { + other.d_ptr = nullptr; + d_ptr->q_ptr = this; + } + + DecoderMMAL::~DecoderMMAL() { + delete d_ptr; + } + + void DecoderMMAL::init(IPacketSource *source, AVRational framerate, AVRational timebase, int64_t duration, AVCodecParameters *vParams, + AVCodecParameters *aParams, AVCodecParameters *sParams) { + Q_UNUSED(aParams) + Q_UNUSED(sParams) + Q_D(AVQt::DecoderMMAL); + Q_UNUSED(source) + if (d->m_pCodecParams) { + avcodec_parameters_free(&d->m_pCodecParams); + } + d->m_pCodecParams = avcodec_parameters_alloc(); + avcodec_parameters_copy(d->m_pCodecParams, vParams); + d->m_duration = duration; + d->m_framerate = framerate; + d->m_timebase = timebase; + { + QMutexLocker lock(&d->m_cbListMutex); + for (const auto &cb: d->m_cbList) { + cb->init(this, framerate, duration); + } + } + init(); + } + + int DecoderMMAL::init() { + return 0; + } + + void DecoderMMAL::deinit(IPacketSource *source) { + Q_UNUSED(source) + deinit(); + } + + int DecoderMMAL::deinit() { + Q_D(AVQt::DecoderMMAL); + + stop(); + + if (d->m_pCodecParams) { + avcodec_parameters_free(&d->m_pCodecParams); + d->m_pCodecParams = nullptr; + } + if (d->m_pCodecCtx) { + avcodec_close(d->m_pCodecCtx); + avcodec_free_context(&d->m_pCodecCtx); + d->m_pCodecCtx = nullptr; + d->m_pCodec = nullptr; + } + + return 0; + } + + void DecoderMMAL::start(IPacketSource *source) { + Q_UNUSED(source) + start(); + } + + int DecoderMMAL::start() { + Q_D(AVQt::DecoderMMAL); + + bool notRunning = false; + if (d->m_running.compare_exchange_strong(notRunning, true)) { + d->m_paused.store(false); + paused(false); + + QThread::start(); + return 0; + } + return -1; + } + + void DecoderMMAL::stop(IPacketSource *source) { + Q_UNUSED(source) + stop(); + } + + int DecoderMMAL::stop() { + Q_D(AVQt::DecoderMMAL); + + bool shouldBeCurrent = true; + if (d->m_running.compare_exchange_strong(shouldBeCurrent, false)) { + { +// QMutexLocker lock(&d->m_cbListMutex); + for (const auto &cb: d->m_cbList) { + cb->stop(this); + } + } + { + QMutexLocker lock(&d->m_inputQueueMutex); + for (auto &packet : d->m_inputQueue) { + av_packet_unref(packet); + av_packet_free(&packet); + } + d->m_inputQueue.clear(); + } + + wait(); + + return 0; + } + + return -1; + } + + void DecoderMMAL::pause(bool pause) { + Q_D(AVQt::DecoderMMAL); + if (d->m_running.load() != pause) { + d->m_paused.store(pause); + + for (const auto &cb: d->m_cbList) { + cb->pause(this, pause); + } + + paused(pause); + } + } + + bool DecoderMMAL::isPaused() { + Q_D(AVQt::DecoderMMAL); + return d->m_paused.load(); + } + + qint64 DecoderMMAL::registerCallback(IFrameSink *frameSink) { + Q_D(AVQt::DecoderMMAL); + + QMutexLocker lock(&d->m_cbListMutex); + if (!d->m_cbList.contains(frameSink)) { + d->m_cbList.append(frameSink); + if (d->m_running.load() && d->m_pCodecCtx) { + frameSink->init(this, d->m_framerate, d->m_duration); + frameSink->start(this); + } + return d->m_cbList.indexOf(frameSink); + } + return -1; + } + + qint64 DecoderMMAL::unregisterCallback(IFrameSink *frameSink) { + Q_D(AVQt::DecoderMMAL); + QMutexLocker lock(&d->m_cbListMutex); + if (d->m_cbList.contains(frameSink)) { + auto result = d->m_cbList.indexOf(frameSink); + d->m_cbList.removeOne(frameSink); + frameSink->stop(this); + frameSink->deinit(this); + return result; + } + return -1; + } + + void DecoderMMAL::onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) { + Q_UNUSED(source) + + Q_D(AVQt::DecoderMMAL); + + if (packetType == IPacketSource::CB_VIDEO) { + AVPacket *queuePacket = av_packet_clone(packet); + while (d->m_inputQueue.size() >= 50) { + QThread::msleep(4); + } + QMutexLocker lock(&d->m_inputQueueMutex); + d->m_inputQueue.append(queuePacket); + } + } + + void DecoderMMAL::run() { + Q_D(AVQt::DecoderMMAL); + + while (d->m_running) { + if (!d->m_paused.load() && !d->m_inputQueue.isEmpty()) { + int ret; + constexpr size_t strBufSize = 64; + char strBuf[strBufSize]; + // If m_pCodecParams is nullptr, it is not initialized by packet source, if video codec context is nullptr, this is the first packet + if (d->m_pCodecParams && !d->m_pCodecCtx) { + switch (d->m_pCodecParams->codec_id) { + case AV_CODEC_ID_H264: + d->m_pCodec = avcodec_find_decoder_by_name("h264_mmal"); + break; + case AV_CODEC_ID_MPEG2VIDEO: + d->m_pCodec = avcodec_find_decoder_by_name("mpeg2_mmal"); + break; + case AV_CODEC_ID_MPEG4: + d->m_pCodec = avcodec_find_decoder_by_name("mpeg4_mmal"); + break; + case AV_CODEC_ID_VC1: + d->m_pCodec = avcodec_find_decoder_by_name("vc1_mmal"); + break; + default: + qFatal("No MMAL decoder found for the specified codec: %s", avcodec_get_name(d->m_pCodecParams->codec_id)); + break; + } + + d->m_pCodecCtx = avcodec_alloc_context3(d->m_pCodec); + if (!d->m_pCodecCtx) { + qFatal("Could not allocate MMAL decoder context, probably out of memory"); + } + + avcodec_parameters_to_context(d->m_pCodecCtx, d->m_pCodecParams); + ret = avcodec_open2(d->m_pCodecCtx, d->m_pCodec, nullptr); + if (ret != 0) { + qFatal("%d: Could not open MMAL decoder: %s", ret, av_make_error_string(strBuf, strBufSize, ret)); + } + + for (const auto &cb: d->m_cbList) { + cb->start(this); + } + started(); + } + { + QMutexLocker lock(&d->m_inputQueueMutex); + while (!d->m_inputQueue.isEmpty()) { + AVPacket *packet = d->m_inputQueue.dequeue(); + lock.unlock(); + + qDebug("Video packet queue size: %d", d->m_inputQueue.size()); + + ret = avcodec_send_packet(d->m_pCodecCtx, packet); + if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) { + lock.relock(); + d->m_inputQueue.prepend(packet); + break; + } else if (ret < 0) { + qFatal("%d: Error sending packet to MMAL decoder: %s", ret, av_make_error_string(strBuf, strBufSize, ret)); + } + av_packet_unref(packet); + av_packet_free(&packet); + lock.relock(); + } + } + AVFrame *frame = av_frame_alloc(); + while (true) { + ret = avcodec_receive_frame(d->m_pCodecCtx, frame); + if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) { + break; + } else if (ret < 0) { + qFatal("%d: Error receiving frame from MMAL decoder: %s", ret, av_make_error_string(strBuf, strBufSize, ret)); + } + +// auto t1 = NOW(); +// AVFrame *swFrame = av_frame_alloc(); +// ret = av_hwframe_transfer_data(swFrame, frame, 0); +// if (ret != 0) { +// qFatal("%d: Could not get hw frame: %s", ret, av_make_error_string(strBuf, strBufSize, ret)); +// } +// auto t3 = NOW(); + + QMutexLocker lock2(&d->m_cbListMutex); +// QList<QFuture<void>> cbFutures; + for (const auto &cb: d->m_cbList) { +// cbFutures.append(QtConcurrent::run([=] { + AVFrame *cbFrame = av_frame_clone(frame); + cbFrame->pts = av_rescale_q(frame->pts, d->m_timebase, + av_make_q(1, 1000000)); // Rescale pts to microseconds for easier processing + qDebug("Calling video frame callback for PTS: %lld, Timebase: %d/%d", static_cast<long long>(cbFrame->pts), + d->m_timebase.num, + d->m_timebase.den); + QTime time = QTime::currentTime(); + cb->onFrame(this, cbFrame, static_cast<int64_t>(av_q2d(av_inv_q(d->m_framerate)) * 1000.0)); + qDebug() << "Video CB time:" << time.msecsTo(QTime::currentTime()); + av_frame_unref(cbFrame); + av_frame_free(&cbFrame); +// })); + } +// bool cbBusy = true; +// while (cbBusy) { +// cbBusy = false; +// for (const auto &future: cbFutures) { +// if (future.isRunning()) { +// cbBusy = true; +// break; +// } +// } +// usleep(500); +// } +// auto t2 = NOW(); +// qDebug("Decoder frame transfer time: %ld us", TIME_US(t1, t3)); + +// av_frame_free(&swFrame); + av_frame_unref(frame); + } + av_frame_free(&frame); + } else { + msleep(4); + } + } + } +} \ No newline at end of file diff --git a/AVQt/filter/DecoderMMAL.h b/AVQt/filter/DecoderMMAL.h new file mode 100644 index 0000000000000000000000000000000000000000..513a1ce81bf15ea5f11297bf22dd6f9da6e91219 --- /dev/null +++ b/AVQt/filter/DecoderMMAL.h @@ -0,0 +1,175 @@ +// +// Created by silas on 3/1/21. +// + +#include "IDecoder.h" + +#include <QtCore> +#include <QtGui> + +extern "C" { +#include <libavutil/avutil.h> +#include <libavutil/hwcontext.h> +#include <libavfilter/avfilter.h> +#include <libavformat/avformat.h> +#include <libavdevice/avdevice.h> +#include <libavcodec/avcodec.h> +#include <libswscale/swscale.h> +#include <libswresample/swresample.h> +#include <libavutil/imgutils.h> +} + + +#ifndef TRANSCODE_DECODERMMAL_H +#define TRANSCODE_DECODERMMAL_H + +namespace AVQt { + class DecoderMMALPrivate; + + /*! + * \class DecoderMMAL + * \brief MMAL accelerated video decoder + * + * Decodes video from packet source into single frames, that are passed to every via registerCallback registered callback. + * It does ***not*** stop when no callbacks are registered, so make sure, + * that either at least one callback is registered or the decoder is paused, or frames will be dropped + */ + class DecoderMMAL : public QThread, public IDecoder { + Q_OBJECT + Q_INTERFACES(AVQt::IDecoder) +// Q_INTERFACES(AVQt::IFrameSource) +// Q_INTERFACES(AVQt::IPacketSink) + + Q_DECLARE_PRIVATE(AVQt::DecoderMMAL) + + public: + + /*! + * Standard public constructor. Creates new instance with given input device. This device can not be changed afterwards, + * because the decoder would have to be completely reinitialized thereafter + * @param inputDevice QIODevice to read video from + * @param parent Pointer to parent QObject for Qt's meta object system + */ + explicit DecoderMMAL(QObject *parent = nullptr); + + DecoderMMAL(DecoderMMAL &&other) noexcept; + + /*! + * \private + */ + DecoderMMAL(const DecoderMMAL &) = delete; + + /*! + * \private + */ + void operator=(const DecoderMMAL &) = delete; + + /*! + * \private + */ + ~DecoderMMAL() Q_DECL_OVERRIDE; + + /*! + * \brief Returns paused state of decoder + * @return Paused state + */ + bool isPaused() Q_DECL_OVERRIDE; + + /*! + * \brief Register frame sink/filter with given callback type \c type + * @param frameSink Frame sink/filter to be added + * @param type One element or some bitwise or combination of elements of IFrameSource::CB_TYPE + * @return Current position in callback list + */ + Q_INVOKABLE qint64 registerCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE; + + /*! + * \brief Removes frame sink/filter from registry + * @param frameSink Frame sink/filter to be removed + * @return Last position in callback list, -1 when not found + */ + Q_INVOKABLE qint64 unregisterCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE; + + public slots: + /*! + * \brief Initialize decoder. Opens input device, creates input contexts. + * @return Status code (0 = Success, LibAV error codes, use av_make_error_string() to get an error message) + */ + Q_INVOKABLE int init() Q_DECL_OVERRIDE; + + /*! + * \brief Clean up decoder. Closes input device, destroys input contexts. + * @return Status code (0 = Success, LibAV error codes, use av_make_error_string() to get an error message) + */ + Q_INVOKABLE int deinit() Q_DECL_OVERRIDE; + + /*! + * \brief Starts decoder. Sets running flag and starts decoding thread. + * @return Status code (0 = Success, LibAV error codes, use av_make_error_string() to get an error message) + */ + Q_INVOKABLE int start() Q_DECL_OVERRIDE; + + /*! + * \brief Stops decoder. Resets running flag and interrupts decoding thread. + * @return Status code (0 = Success, LibAV error codes, use av_make_error_string() to get an error message) + */ + Q_INVOKABLE int stop() Q_DECL_OVERRIDE; + + /*! + * \brief Sets paused flag. When set to true, decoder thread will suspend after processing current frame + * @param pause New paused flag state + */ + Q_INVOKABLE void pause(bool pause) Q_DECL_OVERRIDE; + + + Q_INVOKABLE void + init(IPacketSource *source, AVRational framerate, AVRational timebase, int64_t duration, AVCodecParameters *vParams, + AVCodecParameters *aParams, AVCodecParameters *sParams) Q_DECL_OVERRIDE; + + Q_INVOKABLE void deinit(IPacketSource *source) Q_DECL_OVERRIDE; + + Q_INVOKABLE void start(IPacketSource *source) Q_DECL_OVERRIDE; + + Q_INVOKABLE void stop(IPacketSource *source) Q_DECL_OVERRIDE; + + Q_INVOKABLE void onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) Q_DECL_OVERRIDE; + + signals: + + /*! + * \brief Emitted after start() finished + */ + void started() Q_DECL_OVERRIDE; + + /*! + * \brief Emitted after stop() finished + */ + void stopped() Q_DECL_OVERRIDE; + + /*! + * \brief Emitted when paused state changes + * @param pause Current paused state + */ + void paused(bool pause) Q_DECL_OVERRIDE; + + protected: + /*! + * Protected constructor for use in derived classes to initialize private struct + * @param p Private struct + */ + [[maybe_unused]] explicit DecoderMMAL(DecoderMMALPrivate &p); + + /*! + * \private + */ + void run() Q_DECL_OVERRIDE; + + /*! + * \private + */ + DecoderMMALPrivate *d_ptr; + }; + +} + +#endif //TRANSCODE_DECODERMMAL_H \ No newline at end of file diff --git a/AVQt/filter/DecoderQSV.cpp b/AVQt/filter/DecoderQSV.cpp index 0ed9cf21bf62752952bdc654054c3228382bf02b..464d43c5ed3d288e1e52208cddb857c708fc65bc 100644 --- a/AVQt/filter/DecoderQSV.cpp +++ b/AVQt/filter/DecoderQSV.cpp @@ -9,27 +9,29 @@ #include "input/IPacketSource.h" #include <QApplication> -#include <QtConcurrent> #include <QImage> +//#include <QtConcurrent> -#ifndef DOXYGEN_SHOULD_SKIP_THIS -#define NOW() std::chrono::high_resolution_clock::now() -#define TIME_US(t1, t2) std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count() -#endif +//#ifndef DOXYGEN_SHOULD_SKIP_THIS +//#define NOW() std::chrono::high_resolution_clock::now() +//#define TIME_US(t1, t2) std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count() +//#endif namespace AVQt { DecoderQSV::DecoderQSV(QObject *parent) : QThread(parent), d_ptr(new DecoderQSVPrivate(this)) { - Q_D(AVQt::DecoderQSV); } [[maybe_unused]] DecoderQSV::DecoderQSV(DecoderQSVPrivate &p) : d_ptr(&p) { } - DecoderQSV::~DecoderQSV() { - deinit(); + DecoderQSV::DecoderQSV(DecoderQSV &&other) noexcept: d_ptr(other.d_ptr) { + other.d_ptr = nullptr; + d_ptr->q_ptr = this; + } + DecoderQSV::~DecoderQSV() { delete d_ptr; } @@ -57,8 +59,6 @@ namespace AVQt { } int DecoderQSV::init() { - Q_D(AVQt::DecoderQSV); - return 0; } @@ -158,7 +158,7 @@ namespace AVQt { return d->m_paused.load(); } - int DecoderQSV::registerCallback(IFrameSink *frameSink) { + qint64 DecoderQSV::registerCallback(IFrameSink *frameSink) { Q_D(AVQt::DecoderQSV); QMutexLocker lock(&d->m_cbListMutex); @@ -173,11 +173,11 @@ namespace AVQt { return -1; } - int DecoderQSV::unregisterCallback(IFrameSink *frameSink) { + qint64 DecoderQSV::unregisterCallback(IFrameSink *frameSink) { Q_D(AVQt::DecoderQSV); QMutexLocker lock(&d->m_cbListMutex); if (d->m_cbList.contains(frameSink)) { - int result = d->m_cbList.indexOf(frameSink); + auto result = d->m_cbList.indexOf(frameSink); d->m_cbList.removeOne(frameSink); frameSink->stop(this); frameSink->deinit(this); @@ -187,7 +187,7 @@ namespace AVQt { } void DecoderQSV::onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) { - Q_UNUSED(source); + Q_UNUSED(source) Q_D(AVQt::DecoderQSV); @@ -206,7 +206,7 @@ namespace AVQt { while (d->m_running) { if (!d->m_paused.load() && !d->m_inputQueue.isEmpty()) { - int ret = 0; + int ret; constexpr size_t strBufSize = 64; char strBuf[strBufSize]; // If m_pCodecParams is nullptr, it is not initialized by packet source, if video codec context is nullptr, this is the first packet @@ -244,7 +244,7 @@ namespace AVQt { AVPacket *packet = d->m_inputQueue.dequeue(); lock.unlock(); - qDebug("Video packet queue size: %lld", d->m_inputQueue.size()); + qDebug("Video packet queue size: %d", d->m_inputQueue.size()); ret = avcodec_send_packet(d->m_pCodecCtx, packet); if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) { @@ -283,7 +283,8 @@ namespace AVQt { AVFrame *cbFrame = av_frame_clone(frame); cbFrame->pts = av_rescale_q(frame->pts, d->m_timebase, av_make_q(1, 1000000)); // Rescale pts to microseconds for easier processing - qDebug("Calling video frame callback for PTS: %ld, Timebase: %d/%d", cbFrame->pts, d->m_timebase.num, + qDebug("Calling video frame callback for PTS: %lld, Timebase: %d/%d", static_cast<long long>(cbFrame->pts), + d->m_timebase.num, d->m_timebase.den); QTime time = QTime::currentTime(); cb->onFrame(this, cbFrame, static_cast<int64_t>(av_q2d(av_inv_q(d->m_framerate)) * 1000.0)); diff --git a/AVQt/filter/DecoderQSV.h b/AVQt/filter/DecoderQSV.h index 30a15ac507470295561b7d843e2af3a2b165e623..3d740cf8066c269aef0c21ebf3a2a036359ddd4a 100644 --- a/AVQt/filter/DecoderQSV.h +++ b/AVQt/filter/DecoderQSV.h @@ -52,6 +52,18 @@ namespace AVQt { */ explicit DecoderQSV(QObject *parent = nullptr); + DecoderQSV(DecoderQSV &&other) noexcept; + + /*! + * \private + */ + DecoderQSV(const DecoderQSV &) = delete; + + /*! + * \private + */ + void operator=(const DecoderQSV &) = delete; + /*! * \private */ @@ -69,14 +81,14 @@ namespace AVQt { * @param type One element or some bitwise or combination of elements of IFrameSource::CB_TYPE * @return Current position in callback list */ - Q_INVOKABLE int registerCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE; + Q_INVOKABLE qint64 registerCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE; /*! * \brief Removes frame sink/filter from registry * @param frameSink Frame sink/filter to be removed * @return Last position in callback list, -1 when not found */ - Q_INVOKABLE int unregisterCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE; + Q_INVOKABLE qint64 unregisterCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE; public slots: /*! diff --git a/AVQt/filter/DecoderVAAPI.cpp b/AVQt/filter/DecoderVAAPI.cpp index 7bd0a2e9de25b24eea7a6be0526f9355e1b3918a..a68f3edf229d6faf9bf99d846ac56483e12743b3 100644 --- a/AVQt/filter/DecoderVAAPI.cpp +++ b/AVQt/filter/DecoderVAAPI.cpp @@ -9,27 +9,29 @@ #include "input/IPacketSource.h" #include <QApplication> -#include <QtConcurrent> #include <QImage> +//#include <QtConcurrent> -#ifndef DOXYGEN_SHOULD_SKIP_THIS -#define NOW() std::chrono::high_resolution_clock::now() -#define TIME_US(t1, t2) std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count() -#endif +//#ifndef DOXYGEN_SHOULD_SKIP_THIS +//#define NOW() std::chrono::high_resolution_clock::now() +//#define TIME_US(t1, t2) std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count() +//#endif namespace AVQt { DecoderVAAPI::DecoderVAAPI(QObject *parent) : QThread(parent), d_ptr(new DecoderVAAPIPrivate(this)) { - Q_D(AVQt::DecoderVAAPI); } [[maybe_unused]] DecoderVAAPI::DecoderVAAPI(DecoderVAAPIPrivate &p) : d_ptr(&p) { } - DecoderVAAPI::~DecoderVAAPI() { - deinit(); + DecoderVAAPI::DecoderVAAPI(DecoderVAAPI &&other) noexcept: d_ptr(other.d_ptr) { + other.d_ptr = nullptr; + d_ptr->q_ptr = this; + } + DecoderVAAPI::~DecoderVAAPI() { delete d_ptr; } @@ -57,8 +59,6 @@ namespace AVQt { } int DecoderVAAPI::init() { - Q_D(AVQt::DecoderVAAPI); - return 0; } @@ -158,7 +158,7 @@ namespace AVQt { return d->m_paused.load(); } - int DecoderVAAPI::registerCallback(IFrameSink *frameSink) { + qint64 DecoderVAAPI::registerCallback(IFrameSink *frameSink) { Q_D(AVQt::DecoderVAAPI); QMutexLocker lock(&d->m_cbListMutex); @@ -173,11 +173,11 @@ namespace AVQt { return -1; } - int DecoderVAAPI::unregisterCallback(IFrameSink *frameSink) { + qint64 DecoderVAAPI::unregisterCallback(IFrameSink *frameSink) { Q_D(AVQt::DecoderVAAPI); QMutexLocker lock(&d->m_cbListMutex); if (d->m_cbList.contains(frameSink)) { - int result = d->m_cbList.indexOf(frameSink); + auto result = d->m_cbList.indexOf(frameSink); d->m_cbList.removeOne(frameSink); frameSink->stop(this); frameSink->deinit(this); @@ -187,7 +187,7 @@ namespace AVQt { } void DecoderVAAPI::onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) { - Q_UNUSED(source); + Q_UNUSED(source) Q_D(AVQt::DecoderVAAPI); @@ -206,7 +206,7 @@ namespace AVQt { while (d->m_running) { if (!d->m_paused.load() && !d->m_inputQueue.isEmpty()) { - int ret = 0; + int ret; constexpr size_t strBufSize = 64; char strBuf[strBufSize]; // If m_pCodecParams is nullptr, it is not initialized by packet source, if video codec context is nullptr, this is the first packet @@ -244,7 +244,7 @@ namespace AVQt { AVPacket *packet = d->m_inputQueue.dequeue(); lock.unlock(); - qDebug("Video packet queue size: %lld", d->m_inputQueue.size()); + qDebug("Video packet queue size: %d", d->m_inputQueue.size()); ret = avcodec_send_packet(d->m_pCodecCtx, packet); if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) { @@ -280,16 +280,17 @@ namespace AVQt { // QList<QFuture<void>> cbFutures; for (const auto &cb: d->m_cbList) { // cbFutures.append(QtConcurrent::run([=] { - AVFrame *cbFrame = av_frame_clone(frame); - cbFrame->pts = av_rescale_q(frame->pts, d->m_timebase, - av_make_q(1, 1000000)); // Rescale pts to microseconds for easier processing - qDebug("Calling video frame callback for PTS: %ld, Timebase: %d/%d", cbFrame->pts, d->m_timebase.num, - d->m_timebase.den); - QTime time = QTime::currentTime(); - cb->onFrame(this, cbFrame, static_cast<int64_t>(av_q2d(av_inv_q(d->m_framerate)) * 1000.0)); - qDebug() << "Video CB time:" << time.msecsTo(QTime::currentTime()); - av_frame_unref(cbFrame); - av_frame_free(&cbFrame); + AVFrame *cbFrame = av_frame_clone(frame); + cbFrame->pts = av_rescale_q(frame->pts, d->m_timebase, + av_make_q(1, 1000000)); // Rescale pts to microseconds for easier processing + qDebug("Calling video frame callback for PTS: %lld, Timebase: %d/%d", static_cast<long long>(cbFrame->pts), + d->m_timebase.num, + d->m_timebase.den); + QTime time = QTime::currentTime(); + cb->onFrame(this, cbFrame, static_cast<int64_t>(av_q2d(av_inv_q(d->m_framerate)) * 1000.0)); + qDebug() << "Video CB time:" << time.msecsTo(QTime::currentTime()); + av_frame_unref(cbFrame); + av_frame_free(&cbFrame); // })); } // bool cbBusy = true; diff --git a/AVQt/filter/DecoderVAAPI.h b/AVQt/filter/DecoderVAAPI.h index 3bb315c57201671f9de1564b70ea3116aece62b2..f9170617791c82edd17f5cbaa0210b101ce30150 100644 --- a/AVQt/filter/DecoderVAAPI.h +++ b/AVQt/filter/DecoderVAAPI.h @@ -52,6 +52,18 @@ namespace AVQt { */ explicit DecoderVAAPI(QObject *parent = nullptr); + DecoderVAAPI(DecoderVAAPI &&other) noexcept; + + /*! + * \private + */ + DecoderVAAPI(const DecoderVAAPI &) = delete; + + /*! + * \private + */ + void operator=(const DecoderVAAPI &) = delete; + /*! * \private */ @@ -69,14 +81,14 @@ namespace AVQt { * @param type One element or some bitwise or combination of elements of IFrameSource::CB_TYPE * @return Current position in callback list */ - Q_INVOKABLE int registerCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE; + Q_INVOKABLE qint64 registerCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE; /*! * \brief Removes frame sink/filter from registry * @param frameSink Frame sink/filter to be removed * @return Last position in callback list, -1 when not found */ - Q_INVOKABLE int unregisterCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE; + Q_INVOKABLE qint64 unregisterCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE; public slots: /*! @@ -160,4 +172,4 @@ namespace AVQt { } -#endif //TRANSCODE_DECODERVAAPI_H +#endif //TRANSCODE_DECODERVAAPI_H \ No newline at end of file diff --git a/AVQt/filter/EncoderVAAPI.cpp b/AVQt/filter/EncoderVAAPI.cpp index 7ce43ec0c972f188684b8bb75920865a5038ae9a..1c76568bd995933b4f4dcbc67934052ee35c141b 100644 --- a/AVQt/filter/EncoderVAAPI.cpp +++ b/AVQt/filter/EncoderVAAPI.cpp @@ -5,7 +5,6 @@ #include "private/EncoderVAAPI_p.h" #include "EncoderVAAPI.h" -#include <QtCore> #include "output/IPacketSink.h" namespace AVQt { @@ -19,6 +18,11 @@ namespace AVQt { } + EncoderVAAPI::EncoderVAAPI(EncoderVAAPI &&other) noexcept: d_ptr(other.d_ptr) { + other.d_ptr = nullptr; + d_ptr->q_ptr = this; + } + EncoderVAAPI::~EncoderVAAPI() { delete d_ptr; } @@ -41,6 +45,13 @@ namespace AVQt { int EncoderVAAPI::deinit() { Q_D(AVQt::EncoderVAAPI); + { + QMutexLocker lock(&d->m_cbListMutex); + for (const auto &cb: d->m_cbList) { + cb->deinit(this); + } + } + if (d->m_pDeviceCtx) { av_buffer_unref(&d->m_pDeviceCtx); } @@ -67,6 +78,7 @@ namespace AVQt { bool shouldBe = false; if (d->m_running.compare_exchange_strong(shouldBe, true)) { d->m_paused.store(false); + QThread::start(); started(); @@ -79,7 +91,7 @@ namespace AVQt { Q_D(AVQt::EncoderVAAPI); bool shouldBe = true; - if (d->m_running.compare_exchange_strong(shouldBe, true)) { + if (d->m_running.compare_exchange_strong(shouldBe, false)) { d->m_paused.store(false); { @@ -120,7 +132,7 @@ namespace AVQt { } int EncoderVAAPI::init(IFrameSource *source, AVRational framerate, int64_t duration) { - Q_UNUSED(source); + Q_UNUSED(source) Q_UNUSED(duration) Q_D(AVQt::EncoderVAAPI); d->m_framerate = framerate; @@ -130,32 +142,28 @@ namespace AVQt { int EncoderVAAPI::deinit(IFrameSource *source) { Q_UNUSED(source) - Q_D(AVQt::EncoderVAAPI); deinit(); return 0; } int EncoderVAAPI::start(IFrameSource *source) { Q_UNUSED(source) - Q_D(AVQt::EncoderVAAPI); start(); return 0; } int EncoderVAAPI::stop(IFrameSource *source) { Q_UNUSED(source) - Q_D(AVQt::EncoderVAAPI); stop(); return 0; } void EncoderVAAPI::pause(IFrameSource *source, bool paused) { Q_UNUSED(source) - Q_D(AVQt::EncoderVAAPI); - this->pause(paused); + pause(paused); } - qsizetype EncoderVAAPI::registerCallback(IPacketSink *packetSink, uint8_t type) { + qint64 EncoderVAAPI::registerCallback(IPacketSink *packetSink, int8_t type) { Q_D(AVQt::EncoderVAAPI); if (type != IPacketSource::CB_VIDEO) { @@ -173,7 +181,7 @@ namespace AVQt { } } - qsizetype EncoderVAAPI::unregisterCallback(IPacketSink *packetSink) { + qint64 EncoderVAAPI::unregisterCallback(IPacketSink *packetSink) { Q_D(AVQt::EncoderVAAPI); { @@ -184,7 +192,7 @@ namespace AVQt { } void EncoderVAAPI::onFrame(IFrameSource *source, AVFrame *frame, int64_t frameDuration) { - Q_UNUSED(source); + Q_UNUSED(source) Q_D(AVQt::EncoderVAAPI); QPair<AVFrame *, int64_t> queueFrame{av_frame_alloc(), frameDuration}; @@ -194,6 +202,7 @@ namespace AVQt { case AV_PIX_FMT_DXVA2_VLD: qDebug("Transferring frame from GPU to CPU"); av_hwframe_transfer_data(queueFrame.first, frame, 0); + queueFrame.first->pts = frame->pts; break; default: qDebug("Referencing frame"); @@ -213,7 +222,7 @@ namespace AVQt { void EncoderVAAPI::run() { Q_D(AVQt::EncoderVAAPI); - int ret{0}; + int ret; constexpr auto strBufSize{64}; char strBuf[strBufSize]; @@ -284,19 +293,28 @@ namespace AVQt { // } // d->m_pCodecCtx->pix_fmt = AV_PIX_FMT_VAAPI; - d->m_pCodecCtx->time_base = av_inv_q(d->m_framerate); + d->m_pCodecCtx->time_base = av_make_q(1, 1000000); ret = avcodec_open2(d->m_pCodecCtx, d->m_pCodec, nullptr); if (ret < 0) { qFatal("%i: Unable to open VAAPI encoder: %s", ret, av_make_error_string(strBuf, strBufSize, ret)); } + qDebug("[AVQt::EncoderVAAPI] Encoder timebase: %d/%d", d->m_pCodecCtx->time_base.num, d->m_pCodecCtx->time_base.den); d->m_pHWFrame = av_frame_alloc(); ret = av_hwframe_get_buffer(d->m_pFramesCtx, d->m_pHWFrame, 0); if (ret != 0) { qFatal("%i: Could not allocate HW frame in encoder context: %s", ret, av_make_error_string(strBuf, strBufSize, ret)); } + + QMutexLocker lock(&d->m_cbListMutex); + for (const auto &cb: d->m_cbList) { + AVCodecParameters *parameters = avcodec_parameters_alloc(); + avcodec_parameters_from_context(parameters, d->m_pCodecCtx); + cb->init(this, d->m_framerate, d->m_pCodecCtx->time_base, 0, parameters, nullptr, nullptr); + cb->start(this); + } } - while (!d->m_inputQueue.isEmpty()) { + if (!d->m_inputQueue.isEmpty()) { QPair<AVFrame *, int64_t> frame; { QMutexLocker lock(&d->m_inputQueueMutex); @@ -307,6 +325,7 @@ namespace AVQt { } else { av_hwframe_transfer_data(d->m_pHWFrame, frame.first, 0); } + d->m_pHWFrame->pts = av_rescale_q(frame.first->pts, av_make_q(1, 1000000), d->m_pCodecCtx->time_base); av_frame_free(&frame.first); ret = avcodec_send_frame(d->m_pCodecCtx, d->m_pHWFrame); if (ret != 0) { @@ -316,12 +335,15 @@ namespace AVQt { AVPacket *packet = av_packet_alloc(); while (true) { ret = avcodec_receive_packet(d->m_pCodecCtx, packet); + qDebug("[AVQt::EncoderVAAPI] Got packet from encoder with PTS: %lld, timebase: %d/%d", + static_cast<long long>(packet->pts), + d->m_pCodecCtx->time_base.num, d->m_pCodecCtx->time_base.den); if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) { break; - } else if (ret < 0) { + } else if (ret != 0) { qFatal("%i: Could not receive packet from encoder: %s", ret, av_make_error_string(strBuf, strBufSize, ret)); } - { + if (packet->buf) { QMutexLocker lock(&d->m_cbListMutex); for (const auto &cb: d->m_cbList) { AVPacket *cbPacket = av_packet_clone(packet); @@ -329,6 +351,7 @@ namespace AVQt { av_packet_free(&cbPacket); } } + av_packet_unref(packet); } } else { msleep(1); @@ -340,12 +363,8 @@ namespace AVQt { delete d_ptr; d_ptr = other.d_ptr; other.d_ptr = nullptr; + d_ptr->q_ptr = this; return *this; } - - EncoderVAAPI::EncoderVAAPI(EncoderVAAPI &&other) { - d_ptr = other.d_ptr; - other.d_ptr = nullptr; - } } \ No newline at end of file diff --git a/AVQt/filter/EncoderVAAPI.h b/AVQt/filter/EncoderVAAPI.h index 1b00d8e9ffa0a1fa179504206330a8c4bfc07c6a..617160defbe3ee073a00125e38b0b4cc4d8088e2 100644 --- a/AVQt/filter/EncoderVAAPI.h +++ b/AVQt/filter/EncoderVAAPI.h @@ -34,19 +34,19 @@ namespace AVQt { public: explicit EncoderVAAPI(QString encoder, QObject *parent = nullptr); - explicit EncoderVAAPI(EncoderVAAPI &other) = delete; + EncoderVAAPI(EncoderVAAPI &&other) noexcept; - explicit EncoderVAAPI(EncoderVAAPI &&other); + explicit EncoderVAAPI(EncoderVAAPI &other) = delete; - ~EncoderVAAPI() override; + ~EncoderVAAPI() Q_DECL_OVERRIDE; - bool isPaused() override; + bool isPaused() Q_DECL_OVERRIDE; - qsizetype registerCallback(IPacketSink *packetSink, uint8_t type) override; + qint64 registerCallback(IPacketSink *packetSink, int8_t type) Q_DECL_OVERRIDE; - qsizetype unregisterCallback(IPacketSink *packetSink) override; + qint64 unregisterCallback(IPacketSink *packetSink) Q_DECL_OVERRIDE; - void run() override; + void run() Q_DECL_OVERRIDE; EncoderVAAPI &operator=(const EncoderVAAPI &other) = delete; @@ -54,35 +54,35 @@ namespace AVQt { public slots: - Q_INVOKABLE int init() override; + Q_INVOKABLE int init() Q_DECL_OVERRIDE; - Q_INVOKABLE int deinit() override; + Q_INVOKABLE int deinit() Q_DECL_OVERRIDE; - Q_INVOKABLE int start() override; + Q_INVOKABLE int start() Q_DECL_OVERRIDE; - Q_INVOKABLE int stop() override; + Q_INVOKABLE int stop() Q_DECL_OVERRIDE; - Q_INVOKABLE void pause(bool pause) override; + Q_INVOKABLE void pause(bool pause) Q_DECL_OVERRIDE; - Q_INVOKABLE int init(IFrameSource *source, AVRational framerate, int64_t duration) override; + Q_INVOKABLE int init(IFrameSource *source, AVRational framerate, int64_t duration) Q_DECL_OVERRIDE; - Q_INVOKABLE int deinit(IFrameSource *source) override; + Q_INVOKABLE int deinit(IFrameSource *source) Q_DECL_OVERRIDE; - Q_INVOKABLE int start(IFrameSource *source) override; + Q_INVOKABLE int start(IFrameSource *source) Q_DECL_OVERRIDE; - Q_INVOKABLE int stop(IFrameSource *source) override; + Q_INVOKABLE int stop(IFrameSource *source) Q_DECL_OVERRIDE; - Q_INVOKABLE void pause(IFrameSource *source, bool paused) override; + Q_INVOKABLE void pause(IFrameSource *source, bool paused) Q_DECL_OVERRIDE; - Q_INVOKABLE void onFrame(IFrameSource *source, AVFrame *frame, int64_t frameDuration) override; + Q_INVOKABLE void onFrame(IFrameSource *source, AVFrame *frame, int64_t frameDuration) Q_DECL_OVERRIDE; signals: - void started() override; + void started() Q_DECL_OVERRIDE; - void stopped() override; + void stopped() Q_DECL_OVERRIDE; - void paused(bool pause) override; + void paused(bool pause) Q_DECL_OVERRIDE; protected: [[maybe_unused]] explicit EncoderVAAPI(EncoderVAAPIPrivate &p); diff --git a/AVQt/filter/IDecoder.h b/AVQt/filter/IDecoder.h index 2751e39d00f9b157f3468901178c73a616e0ad77..f0dfb84aa4c3d619955c3520ac73a3627656aa28 100644 --- a/AVQt/filter/IDecoder.h +++ b/AVQt/filter/IDecoder.h @@ -9,38 +9,54 @@ #define LIBAVQT_DECODER_H namespace AVQt { - class IDecoder: public IFrameSource, public IPacketSink { + class IDecoder : public IFrameSource, public IPacketSink { // IPacketSink interface public: - ~IDecoder() override {}; - virtual bool isPaused() override = 0; - virtual void init(IPacketSource *source, AVRational framerate, AVRational timebase, int64_t duration, AVCodecParameters *vParams, AVCodecParameters *aParams, AVCodecParameters *sParams) override = 0; - virtual void deinit(IPacketSource *source) override = 0; - virtual void start(IPacketSource *source) override = 0; - virtual void stop(IPacketSource *source) override = 0; - virtual void pause(bool paused) override = 0; - virtual void onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) override = 0; + ~IDecoder() Q_DECL_OVERRIDE {}; + + virtual bool isPaused() Q_DECL_OVERRIDE = 0; + + virtual void init(IPacketSource *source, AVRational framerate, AVRational timebase, int64_t duration, AVCodecParameters *vParams, + AVCodecParameters *aParams, AVCodecParameters *sParams) Q_DECL_OVERRIDE = 0; + + virtual void deinit(IPacketSource *source) Q_DECL_OVERRIDE = 0; + + virtual void start(IPacketSource *source) Q_DECL_OVERRIDE = 0; + + virtual void stop(IPacketSource *source) Q_DECL_OVERRIDE = 0; + + virtual void pause(bool paused) Q_DECL_OVERRIDE = 0; + + virtual void onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) Q_DECL_OVERRIDE = 0; signals: - virtual void started() override = 0; - virtual void stopped() override = 0; + + virtual void started() Q_DECL_OVERRIDE = 0; + + virtual void stopped() Q_DECL_OVERRIDE = 0; // IFrameSource interface public: - virtual int registerCallback(IFrameSink *frameSink) override = 0; - virtual int unregisterCallback(IFrameSink *frameSink) override = 0; - virtual int init() override = 0; - virtual int deinit() override = 0; - virtual int start() override = 0; - virtual int stop() override = 0; + virtual qint64 registerCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE = 0; + + virtual qint64 unregisterCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE = 0; + + virtual int init() Q_DECL_OVERRIDE = 0; + + virtual int deinit() Q_DECL_OVERRIDE = 0; + + virtual int start() Q_DECL_OVERRIDE = 0; + + virtual int stop() Q_DECL_OVERRIDE = 0; signals: - virtual void paused(bool pause) override = 0; + + virtual void paused(bool pause) Q_DECL_OVERRIDE = 0; }; } Q_DECLARE_INTERFACE(AVQt::IDecoder, "AVQt::IDecoder") -#endif //LIBAVQT_DECODER_H +#endif //LIBAVQT_DECODER_H \ No newline at end of file diff --git a/AVQt/filter/IEncoder.h b/AVQt/filter/IEncoder.h index a262a025e7777991a3b0afdf49759c2d797a04d3..0554f17f0783899c26f1420fff0cae824e2b1e66 100644 --- a/AVQt/filter/IEncoder.h +++ b/AVQt/filter/IEncoder.h @@ -46,9 +46,9 @@ namespace AVQt { Q_INVOKABLE virtual void pause(bool pause) = 0; - Q_INVOKABLE virtual qsizetype registerCallback(IPacketSink *packetSink, uint8_t type) = 0; + Q_INVOKABLE virtual qint64 registerCallback(IPacketSink *packetSink, int8_t type) = 0; - Q_INVOKABLE virtual qsizetype unregisterCallback(IPacketSink *packetSink) = 0; + Q_INVOKABLE virtual qint64 unregisterCallback(IPacketSink *packetSink) = 0; signals: diff --git a/AVQt/filter/private/AudioDecoder_p.h b/AVQt/filter/private/AudioDecoder_p.h index b766659c69a94e49902c1e37341165971a64ed80..9e11a3e02456cb3c2e08b3c17efa0fe88d282585 100644 --- a/AVQt/filter/private/AudioDecoder_p.h +++ b/AVQt/filter/private/AudioDecoder_p.h @@ -20,29 +20,35 @@ extern "C" { namespace AVQt { class AudioDecoderPrivate { + public: + AudioDecoderPrivate(const AudioDecoderPrivate &) = delete; + + void operator=(const AudioDecoderPrivate &) = delete; + + private: explicit AudioDecoderPrivate(AudioDecoder *q) : q_ptr(q) {}; AudioDecoder *q_ptr; - QMutex m_inputQueueMutex; - QQueue<AVPacket *> m_inputQueue; - int64_t m_duration {0}; + QMutex m_inputQueueMutex{}; + QQueue<AVPacket *> m_inputQueue{}; + int64_t m_duration{0}; - AVCodecParameters *m_pCodecParams {nullptr}; - AVCodec *m_pCodec {nullptr}; - AVCodecContext *m_pCodecCtx {nullptr}; - AVRational m_timebase; + AVCodecParameters *m_pCodecParams{nullptr}; + AVCodec *m_pCodec{nullptr}; + AVCodecContext *m_pCodecCtx{nullptr}; + AVRational m_timebase{0, 1}; // Callback stuff - QMutex m_cbListMutex; - QList<IAudioSink *> m_cbList; + QMutex m_cbListMutex{}; + QList<IAudioSink *> m_cbList{}; // Threading stuff - std::atomic_bool m_running {false}; - std::atomic_bool m_paused {false}; + std::atomic_bool m_running{false}; + std::atomic_bool m_paused{false}; friend class AudioDecoder; }; } -#endif //LIBAVQT_AUDIODECODER_P_H +#endif //LIBAVQT_AUDIODECODER_P_H \ No newline at end of file diff --git a/AVQt/filter/private/DecoderDXVA2_p.h b/AVQt/filter/private/DecoderDXVA2_p.h index c532b1ca1cb7d7ce8541406dd5dd8f2d50293c9f..87c8cc3f23d40035984c762d8002c4db11fb1138 100644 --- a/AVQt/filter/private/DecoderDXVA2_p.h +++ b/AVQt/filter/private/DecoderDXVA2_p.h @@ -16,6 +16,13 @@ namespace AVQt { * \private */ class DecoderDXVA2Private { + public: + DecoderDXVA2Private(const DecoderDXVA2Private &) = delete; + + void operator=(const DecoderDXVA2Private &) = delete; + + private: + explicit DecoderDXVA2Private(DecoderDXVA2 *q) : q_ptr(q) {}; DecoderDXVA2 *q_ptr; diff --git a/AVQt/filter/private/DecoderMMAL_p.h b/AVQt/filter/private/DecoderMMAL_p.h new file mode 100644 index 0000000000000000000000000000000000000000..02028a6437621451bd96dd7033737d267c820d33 --- /dev/null +++ b/AVQt/filter/private/DecoderMMAL_p.h @@ -0,0 +1,54 @@ +/*! + * \private + * \internal + */ + +#include "../DecoderMMAL.h" + +extern "C" { +#include <libavutil/rational.h> +#include <libavutil/frame.h> +} + + +#ifndef TRANSCODE_DECODERMMAL_P_H +#define TRANSCODE_DECODERMMAL_P_H + +namespace AVQt { + /*! + * \private + */ + class DecoderMMALPrivate { + public: + DecoderMMALPrivate(const DecoderMMALPrivate &) = delete; + + void operator=(const DecoderMMALPrivate &) = delete; + + private: + explicit DecoderMMALPrivate(DecoderMMAL *q) : q_ptr(q) {}; + + DecoderMMAL *q_ptr; + + QMutex m_inputQueueMutex{}; + QQueue<AVPacket *> m_inputQueue{}; + int64_t m_duration{0}; + AVRational m_framerate{}; + AVRational m_timebase{}; + + AVCodec *m_pCodec{nullptr}; + AVCodecParameters *m_pCodecParams{nullptr}; + AVCodecContext *m_pCodecCtx{nullptr}; + + // Callback stuff + QMutex m_cbListMutex{}; + QList<IFrameSink *> m_cbList{}; + + // Threading stuff + std::atomic_bool m_running{false}; + std::atomic_bool m_paused{false}; + + friend class DecoderMMAL; + }; +} + +#endif //TRANSCODE_DECODERMMAL_P_H \ No newline at end of file diff --git a/AVQt/filter/private/DecoderQSV_p.h b/AVQt/filter/private/DecoderQSV_p.h index 0e4112760821a0f900aa6b1b922802cddcc4985d..ef82bc0524eda150b17de6eb0fc7d0a05b6761be 100644 --- a/AVQt/filter/private/DecoderQSV_p.h +++ b/AVQt/filter/private/DecoderQSV_p.h @@ -18,15 +18,21 @@ namespace AVQt { * \private */ class DecoderQSVPrivate { + public: + DecoderQSVPrivate(const DecoderQSVPrivate &) = delete; + + void operator=(const DecoderQSVPrivate &) = delete; + + private: explicit DecoderQSVPrivate(DecoderQSV *q) : q_ptr(q) {}; DecoderQSV *q_ptr; - QMutex m_inputQueueMutex; - QQueue<AVPacket *> m_inputQueue; + QMutex m_inputQueueMutex{}; + QQueue<AVPacket *> m_inputQueue{}; int64_t m_duration{0}; - AVRational m_framerate; - AVRational m_timebase; + AVRational m_framerate{}; + AVRational m_timebase{}; AVCodec *m_pCodec{nullptr}; AVCodecParameters *m_pCodecParams{nullptr}; @@ -34,8 +40,8 @@ namespace AVQt { AVBufferRef *m_pDeviceCtx{nullptr}; // Callback stuff - QMutex m_cbListMutex; - QList<IFrameSink *> m_cbList; + QMutex m_cbListMutex{}; + QList<IFrameSink *> m_cbList{}; // Threading stuff std::atomic_bool m_running{false}; diff --git a/AVQt/filter/private/DecoderVAAPI_p.h b/AVQt/filter/private/DecoderVAAPI_p.h index 12cf4c882a9aabe536cbbcdaebbb4c435cc8f494..c247ecbc04cb2b2636ed6e61f67dd36e33dbf847 100644 --- a/AVQt/filter/private/DecoderVAAPI_p.h +++ b/AVQt/filter/private/DecoderVAAPI_p.h @@ -7,6 +7,7 @@ extern "C" { #include <libavutil/rational.h> +#include <libavutil/frame.h> } @@ -18,15 +19,21 @@ namespace AVQt { * \private */ class DecoderVAAPIPrivate { + public: + DecoderVAAPIPrivate(const DecoderVAAPIPrivate &) = delete; + + void operator=(const DecoderVAAPIPrivate &) = delete; + + private: explicit DecoderVAAPIPrivate(DecoderVAAPI *q) : q_ptr(q) {}; DecoderVAAPI *q_ptr; - QMutex m_inputQueueMutex; - QQueue<AVPacket *> m_inputQueue; + QMutex m_inputQueueMutex{}; + QQueue<AVPacket *> m_inputQueue{}; int64_t m_duration{0}; - AVRational m_framerate; - AVRational m_timebase; + AVRational m_framerate{}; + AVRational m_timebase{}; AVCodec *m_pCodec{nullptr}; AVCodecParameters *m_pCodecParams{nullptr}; @@ -34,8 +41,8 @@ namespace AVQt { AVBufferRef *m_pDeviceCtx{nullptr}; // Callback stuff - QMutex m_cbListMutex; - QList<IFrameSink *> m_cbList; + QMutex m_cbListMutex{}; + QList<IFrameSink *> m_cbList{}; // Threading stuff std::atomic_bool m_running{false}; @@ -45,4 +52,4 @@ namespace AVQt { }; } -#endif //TRANSCODE_DECODERVAAPI_P_H \ No newline at end of file +#endif //TRANSCODE_DECODERVAAPI_P_H diff --git a/AVQt/filter/private/EncoderVAAPI_p.h b/AVQt/filter/private/EncoderVAAPI_p.h index d7be7f1186a6113d8c3a5887f39fb4ea5fd59c77..239bd63c8fde9d4705cdf6cfab0dc1cf98660e51 100644 --- a/AVQt/filter/private/EncoderVAAPI_p.h +++ b/AVQt/filter/private/EncoderVAAPI_p.h @@ -11,6 +11,12 @@ namespace AVQt { class EncoderVAAPIPrivate { + public: + EncoderVAAPIPrivate(const EncoderVAAPIPrivate &) = delete; + + void operator=(const EncoderVAAPIPrivate &) = delete; + + private: explicit EncoderVAAPIPrivate(EncoderVAAPI *q) : q_ptr(q) {}; EncoderVAAPI *q_ptr; @@ -23,11 +29,11 @@ namespace AVQt { AVBufferRef *m_pDeviceCtx{nullptr}, *m_pFramesCtx{nullptr}; AVFrame *m_pHWFrame{nullptr}; - QMutex m_inputQueueMutex; - QQueue<QPair<AVFrame *, int64_t>> m_inputQueue; + QMutex m_inputQueueMutex{}; + QQueue<QPair<AVFrame *, int64_t>> m_inputQueue{}; - QMutex m_cbListMutex; - QList<IPacketSink *> m_cbList; + QMutex m_cbListMutex{}; + QList<IPacketSink *> m_cbList{}; std::atomic_bool m_running{false}, m_paused{false}, m_firstFrame{true}; diff --git a/AVQt/input/Demuxer.cpp b/AVQt/input/Demuxer.cpp index a4fc9030b7141c2a48661b3f524ef5120e970f06..d99e134901427e16e18385b13a8bdb749967497e 100644 --- a/AVQt/input/Demuxer.cpp +++ b/AVQt/input/Demuxer.cpp @@ -15,8 +15,6 @@ namespace AVQt { } Demuxer::Demuxer(DemuxerPrivate &p) : d_ptr(&p) { - Q_D(AVQt::Demuxer); - } void Demuxer::pause(bool pause) { @@ -34,7 +32,7 @@ namespace AVQt { return d->m_paused.load(); } - qsizetype Demuxer::registerCallback(IPacketSink *packetSink, uint8_t type) { + qint64 Demuxer::registerCallback(IPacketSink *packetSink, int8_t type) { Q_D(AVQt::Demuxer); QMutexLocker lock(&d->m_cbMutex); @@ -59,8 +57,9 @@ namespace AVQt { sParams = avcodec_parameters_alloc(); avcodec_parameters_copy(sParams, d->m_pFormatCtx->streams[d->m_subtitleStream]->codecpar); } - packetSink->init(this, d->m_pFormatCtx->streams[d->m_videoStream]->avg_frame_rate, AVRational(), - d->m_pFormatCtx->duration * 1000.0 / AV_TIME_BASE, vParams, aParams, sParams); + packetSink->init(this, d->m_pFormatCtx->streams[d->m_videoStream]->avg_frame_rate, av_make_q(1, AV_TIME_BASE), + static_cast<int64_t>(static_cast<double>(d->m_pFormatCtx->duration) * 1000.0 / AV_TIME_BASE), vParams, aParams, + sParams); if (vParams) { avcodec_parameters_free(&vParams); } @@ -78,7 +77,7 @@ namespace AVQt { return 0; } - qsizetype Demuxer::unregisterCallback(IPacketSink *packetSink) { + qint64 Demuxer::unregisterCallback(IPacketSink *packetSink) { Q_D(AVQt::Demuxer); QMutexLocker lock(&d->m_cbMutex); @@ -118,7 +117,7 @@ namespace AVQt { avformat_find_stream_info(d->m_pFormatCtx, nullptr); // } - for (size_t si = 0; si < d->m_pFormatCtx->nb_streams; ++si) { + for (int64_t si = 0; si < d->m_pFormatCtx->nb_streams; ++si) { switch (d->m_pFormatCtx->streams[si]->codecpar->codec_type) { case AVMEDIA_TYPE_VIDEO: d->m_videoStreams.append(si); @@ -163,17 +162,20 @@ namespace AVQt { if (vParams) { cb->init(this, d->m_pFormatCtx->streams[d->m_videoStream]->avg_frame_rate, d->m_pFormatCtx->streams[d->m_videoStream]->time_base, - d->m_pFormatCtx->duration * 1000.0 / AV_TIME_BASE, vParams, nullptr, nullptr); + static_cast<int64_t>(static_cast<double>(d->m_pFormatCtx->duration) * 1000.0 / AV_TIME_BASE), vParams, nullptr, + nullptr); } if (aParams) { cb->init(this, d->m_pFormatCtx->streams[d->m_audioStream]->avg_frame_rate, d->m_pFormatCtx->streams[d->m_audioStream]->time_base, - d->m_pFormatCtx->duration * 1000.0 / AV_TIME_BASE, nullptr, aParams, nullptr); + static_cast<int64_t>(static_cast<double>(d->m_pFormatCtx->duration) * 1000.0 / AV_TIME_BASE), nullptr, aParams, + nullptr); } if (sParams) { cb->init(this, d->m_pFormatCtx->streams[d->m_subtitleStream]->avg_frame_rate, d->m_pFormatCtx->streams[d->m_subtitleStream]->time_base, - d->m_pFormatCtx->duration * 1000.0 / AV_TIME_BASE, nullptr, nullptr, sParams); + static_cast<int64_t>(static_cast<double>(d->m_pFormatCtx->duration) * 1000.0 / AV_TIME_BASE), nullptr, nullptr, + sParams); } if (vParams) { avcodec_parameters_free(&vParams); @@ -200,7 +202,6 @@ namespace AVQt { // avio_closep(&d->m_pIOCtx); avformat_close_input(&d->m_pFormatCtx); - delete[] d->m_pBuffer; d->m_videoStreams.clear(); d->m_audioStreams.clear(); @@ -300,9 +301,12 @@ namespace AVQt { aP = QString("%1").arg(audioPackets, 12).toLocal8Bit(); vP = QString("%1").arg(videoPackets, 12).toLocal8Bit(); sP = QString("%1").arg(sttPackets, 12).toLocal8Bit(); - aR = QString("%1").arg((audioPackets * 1.0 / packetCount) * 100.0, 10).toLocal8Bit(); - vR = QString("%1").arg((videoPackets * 1.0 / packetCount) * 100.0, 10).toLocal8Bit(); - sR = QString("%1").arg((sttPackets * 1.0 / packetCount) * 100.0, 10).toLocal8Bit(); + aR = QString("%1").arg((static_cast<double>(audioPackets) * 1.0 / static_cast<double>(packetCount)) * 100.0, + 10).toLocal8Bit(); + vR = QString("%1").arg((static_cast<double>(videoPackets) * 1.0 / static_cast<double>(packetCount)) * 100.0, + 10).toLocal8Bit(); + sR = QString("%1").arg((static_cast<double>(sttPackets) * 1.0 / static_cast<double>(packetCount)) * 100.0, + 10).toLocal8Bit(); qDebug() << "Packet statistics"; qDebug() << "| Packet type | Packet count | Percentage |"; @@ -318,19 +322,9 @@ namespace AVQt { } } - Demuxer::Demuxer(Demuxer &&other) { - d_ptr = other.d_ptr; - d_ptr->q_ptr = this; + Demuxer::Demuxer(Demuxer &&other) noexcept: d_ptr(other.d_ptr) { other.d_ptr = nullptr; - } - - Demuxer &Demuxer::operator=(Demuxer &&other) noexcept { - delete d_ptr; - d_ptr = other.d_ptr; d_ptr->q_ptr = this; - other.d_ptr = nullptr; - - return *this; } int DemuxerPrivate::readFromIO(void *opaque, uint8_t *buf, int bufSize) { @@ -340,7 +334,7 @@ namespace AVQt { if (bytesRead == 0) { return AVERROR_EOF; } else { - return bytesRead; + return static_cast<int>(bytesRead); } } diff --git a/AVQt/input/Demuxer.h b/AVQt/input/Demuxer.h index 9ab24afa9987055adbf73ea7dc81b2b9df7e9351..313c9bed4e5e1c22748c00c5fec91687b457c30e 100644 --- a/AVQt/input/Demuxer.h +++ b/AVQt/input/Demuxer.h @@ -20,11 +20,13 @@ namespace AVQt { Q_DECLARE_PRIVATE(AVQt::Demuxer) public: - [[maybe_unused]] explicit Demuxer(QIODevice *inputDevice, QObject *parent = nullptr); + explicit Demuxer(QIODevice *inputDevice, QObject *parent = nullptr); explicit Demuxer(Demuxer &other) = delete; - explicit Demuxer(Demuxer &&other); + Demuxer &operator=(const Demuxer &other) = delete; + + Demuxer(Demuxer &&other) noexcept; /*! * \private @@ -43,18 +45,14 @@ namespace AVQt { * @param type Callback type, can be linked with bitwise or to set multiple options * @return */ - Q_INVOKABLE qsizetype registerCallback(IPacketSink *packetSink, uint8_t type) Q_DECL_OVERRIDE; + Q_INVOKABLE qint64 registerCallback(IPacketSink *packetSink, int8_t type) Q_DECL_OVERRIDE; /*! * \brief Removes packet callback \c packetSink from registry * @param packetSink Packet sink/filter to be removed * @return Previous position of the item, is -1 when not in registry */ - Q_INVOKABLE qsizetype unregisterCallback(IPacketSink *packetSink) Q_DECL_OVERRIDE; - - Demuxer &operator=(const Demuxer &other) = delete; - - Demuxer &operator=(Demuxer &&other) noexcept; + Q_INVOKABLE qint64 unregisterCallback(IPacketSink *packetSink) Q_DECL_OVERRIDE; public slots: /*! @@ -79,7 +77,7 @@ namespace AVQt { * \brief Stops packet source (e.g. Interrupt processing thread, free camera). * @return Status code (0 = Success) */ - Q_INVOKABLE int stop(); + Q_INVOKABLE int stop() Q_DECL_OVERRIDE; /*! * \brief Sets paused flag of packet source diff --git a/AVQt/input/IAudioSource.h b/AVQt/input/IAudioSource.h index 76e30c1e18a3366b7fa1f7ebb817b6cff4861186..9d563e134a105deaf3531e306bad3f71ec8989eb 100644 --- a/AVQt/input/IAudioSource.h +++ b/AVQt/input/IAudioSource.h @@ -18,9 +18,9 @@ namespace AVQt { virtual bool isPaused() = 0; - Q_INVOKABLE virtual int registerCallback(IAudioSink *callback) = 0; + Q_INVOKABLE virtual qint64 registerCallback(IAudioSink *callback) = 0; - Q_INVOKABLE virtual int unregisterCallback(IAudioSink *callback) = 0; + Q_INVOKABLE virtual qint64 unregisterCallback(IAudioSink *callback) = 0; public slots: Q_INVOKABLE virtual int init() = 0; diff --git a/AVQt/input/IFrameSource.h b/AVQt/input/IFrameSource.h index fcea49dff7b4c6bd393d2f63c29144ea89a12d8a..2e3c88a44dfe3b0534b003ec3df72cbb972dac7f 100644 --- a/AVQt/input/IFrameSource.h +++ b/AVQt/input/IFrameSource.h @@ -63,14 +63,14 @@ namespace AVQt { * @param type Callback type, can be linked with bitwise or to set multiple options * @return */ - Q_INVOKABLE virtual int registerCallback(IFrameSink *frameSink) = 0; + Q_INVOKABLE virtual qint64 registerCallback(IFrameSink *frameSink) = 0; /*! * \brief Removes frame callback \c frameSink from registry * @param frameSink Frame sink/filter to be removed * @return Previous position of the item, is -1 when not in registry */ - Q_INVOKABLE virtual int unregisterCallback(IFrameSink *frameSink) = 0; + Q_INVOKABLE virtual qint64 unregisterCallback(IFrameSink *frameSink) = 0; public slots: /*! diff --git a/AVQt/input/IPacketSource.h b/AVQt/input/IPacketSource.h index de6ef2c1b1f09c5c842c5b85b90b97f857395f51..e092baf597c99fa3b7252798103921b379a3ca66 100644 --- a/AVQt/input/IPacketSource.h +++ b/AVQt/input/IPacketSource.h @@ -57,14 +57,14 @@ namespace AVQt { * @param type Callback type, can be linked with bitwise or to set multiple options * @return */ - Q_INVOKABLE virtual qsizetype registerCallback(IPacketSink *packetSink, uint8_t type) = 0; + Q_INVOKABLE virtual qint64 registerCallback(IPacketSink *packetSink, int8_t type) = 0; /*! * \brief Removes packet callback \c packetSink from registry * @param packetSink Packet sink/filter to be removed * @return Previous position of the item, is -1 when not in registry */ - Q_INVOKABLE virtual qsizetype unregisterCallback(IPacketSink *packetSink) = 0; + Q_INVOKABLE virtual qint64 unregisterCallback(IPacketSink *packetSink) = 0; public slots: /*! diff --git a/AVQt/input/private/Demuxer_p.h b/AVQt/input/private/Demuxer_p.h index ff733d2a4c22c0ffc201764bf32a0905959d4d07..aa6cc518b355539e34baecb91b801520db1bc449 100644 --- a/AVQt/input/private/Demuxer_p.h +++ b/AVQt/input/private/Demuxer_p.h @@ -16,6 +16,12 @@ extern "C" { namespace AVQt { class DemuxerPrivate { + public: + DemuxerPrivate(const DemuxerPrivate &) = delete; + + void operator=(const DemuxerPrivate &) = delete; + + private: explicit DemuxerPrivate(Demuxer *q) : q_ptr(q) {}; static int readFromIO(void *opaque, uint8_t *buf, int bufSize); @@ -24,18 +30,18 @@ namespace AVQt { Demuxer *q_ptr; - std::atomic_bool m_running {false}, m_paused {false}, m_initialized {false}; - QMutex m_cbMutex; - QMap<IPacketSink *, int8_t> m_cbMap; + std::atomic_bool m_running{false}, m_paused{false}, m_initialized{false}; + QMutex m_cbMutex{}; + QMap<IPacketSink *, int8_t> m_cbMap{}; - QList<int> m_videoStreams, m_audioStreams, m_subtitleStreams; - int m_videoStream = -1, m_audioStream = -1, m_subtitleStream = -1; + QList<int64_t> m_videoStreams{}, m_audioStreams{}, m_subtitleStreams{}; + int64_t m_videoStream{-1}, m_audioStream{-1}, m_subtitleStream{-1}; - static constexpr size_t BUFFER_SIZE = 1024; - uint8_t *m_pBuffer {nullptr}; - AVFormatContext *m_pFormatCtx {nullptr}; - AVIOContext *m_pIOCtx {nullptr}; - QIODevice *m_inputDevice {nullptr}; + static constexpr size_t BUFFER_SIZE{1024}; + uint8_t *m_pBuffer{nullptr}; + AVFormatContext *m_pFormatCtx{nullptr}; + AVIOContext *m_pIOCtx{nullptr}; + QIODevice *m_inputDevice{nullptr}; friend class Demuxer; }; diff --git a/AVQt/output/IPacketSink.h b/AVQt/output/IPacketSink.h index 5e9ee3c0ee2378bf0fb7c10f6e4b76d88dbf1339..d922961aa0a66c3018223bc61e134156da4a8056 100644 --- a/AVQt/output/IPacketSink.h +++ b/AVQt/output/IPacketSink.h @@ -44,7 +44,7 @@ namespace AVQt { Q_INVOKABLE virtual void stop(IPacketSource *source) = 0; - Q_INVOKABLE virtual void pause(bool paused) = 0; + Q_INVOKABLE virtual void pause(bool p) = 0; /*! * \brief Callback method, is called for every registered packet type. @@ -61,6 +61,8 @@ namespace AVQt { virtual void started() = 0; virtual void stopped() = 0; + + virtual void paused(bool pause) = 0; }; } diff --git a/AVQt/output/Muxer.cpp b/AVQt/output/Muxer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..935ff345d5ff7b3d7e615227e9d1b953608c5550 --- /dev/null +++ b/AVQt/output/Muxer.cpp @@ -0,0 +1,267 @@ +// +// Created by silas on 5/24/21. +// + +#include <input/IPacketSource.h> +#include "private/Muxer_p.h" +#include "Muxer.h" + +namespace AVQt { + Muxer::Muxer(QIODevice *outputDevice, QObject *parent) : QThread(parent), d_ptr(new MuxerPrivate(this)) { + Q_D(AVQt::Muxer); + d->m_outputDevice = outputDevice; + } + + Muxer::Muxer(AVQt::MuxerPrivate &p) : d_ptr(&p) { + + } + + AVQt::Muxer::Muxer(Muxer &&other) noexcept: d_ptr(other.d_ptr) { + other.d_ptr = nullptr; + } + + bool Muxer::isPaused() { + Q_D(AVQt::Muxer); + return d->m_paused.load(); + } + + void Muxer::init(IPacketSource *source, AVRational framerate, AVRational timebase, int64_t duration, AVCodecParameters *vParams, + AVCodecParameters *aParams, AVCodecParameters *sParams) { + Q_UNUSED(framerate) + Q_UNUSED(duration) + Q_D(AVQt::Muxer); + + if ((vParams && aParams) || (vParams && sParams) || (aParams && sParams)) { + qWarning("[AVQt::Muxer] init() called for multiple stream types at once. Ignoring call"); + return; + } + + if (d->m_sourceStreamMap.contains(source)) { + bool alreadyCalled = false; + for (const auto &stream: d->m_sourceStreamMap[source].keys()) { + if ((vParams && stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) || + (aParams && stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) || + (sParams && stream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)) { + alreadyCalled = true; + break; + } + } + if (alreadyCalled) { + qWarning("[AVQt::Muxer] init() called multiple times for the same stream type by the same source. Ignoring call"); + return; + } + } + QMutexLocker lock(&d->m_initMutex); + if (!d->m_pFormatContext) { + if (!d->m_outputDevice->isOpen()) { + if (!d->m_outputDevice->open((d->m_outputDevice->isSequential() ? QIODevice::WriteOnly : QIODevice::ReadWrite))) { + qFatal("[AVQt::Muxer] Could not open output device"); + } + } else if (!d->m_outputDevice->isWritable()) { + qFatal("[AVQt::Muxer] Output device is not writable"); + } + d->m_pFormatContext = avformat_alloc_context(); + d->m_pFormatContext->oformat = av_guess_format("mpegts", "", nullptr); + d->m_pIOBuffer = static_cast<uint8_t *>(av_malloc(MuxerPrivate::IOBUF_SIZE)); + d->m_pIOContext = avio_alloc_context(d->m_pIOBuffer, MuxerPrivate::IOBUF_SIZE, 1, d->m_outputDevice, nullptr, + &MuxerPrivate::writeToIO, &MuxerPrivate::seekIO); + d->m_pIOContext->seekable = !d->m_outputDevice->isSequential(); + d->m_pFormatContext->pb = d->m_pIOContext; + d->m_pFormatContext->flags |= AVFMT_FLAG_CUSTOM_IO; + } + + if (!d->m_sourceStreamMap.contains(source)) { + d->m_sourceStreamMap[source] = QMap<AVStream *, AVRational>(); + } + if (vParams) { + AVStream *videoStream = avformat_new_stream(d->m_pFormatContext, avcodec_find_encoder(vParams->codec_id)); + videoStream->codecpar = avcodec_parameters_alloc(); + avcodec_parameters_copy(videoStream->codecpar, vParams); + videoStream->time_base = timebase; + d->m_sourceStreamMap[source].insert(videoStream, timebase); + } else if (aParams) { + AVStream *audioStream = avformat_new_stream(d->m_pFormatContext, avcodec_find_encoder(aParams->codec_id)); + audioStream->codecpar = avcodec_parameters_alloc(); + avcodec_parameters_copy(audioStream->codecpar, aParams); + audioStream->time_base = timebase; + d->m_sourceStreamMap[source].insert(audioStream, timebase); + } else if (sParams) { + AVStream *subtitleStream = avformat_new_stream(d->m_pFormatContext, avcodec_find_encoder(sParams->codec_id)); + subtitleStream->codecpar = avcodec_parameters_alloc(); + avcodec_parameters_copy(subtitleStream->codecpar, sParams); + subtitleStream->time_base = timebase; + d->m_sourceStreamMap[source].insert(subtitleStream, timebase); + } + int ret = avformat_init_output(d->m_pFormatContext, nullptr); + if (ret <= 0) { + constexpr auto strBufSize = 32; + char strBuf[strBufSize]; + qWarning("[AVQt::Muxer] %d: Couldn't init AVFormatContext: %s", ret, av_make_error_string(strBuf, strBufSize, ret)); + } + } + + void Muxer::deinit(IPacketSource *source) { + Q_D(AVQt::Muxer); + + stop(nullptr); + + if (d->m_sourceStreamMap.contains(source)) { + d->m_sourceStreamMap.remove(source); + } else { + qWarning("[AVQt::Muxer] deinit() called without preceding init() from source. Ignoring call"); + return; + } + + if (d->m_sourceStreamMap.isEmpty()) { + QMutexLocker lock(&d->m_initMutex); + if (d->m_pFormatContext) { + if (d->m_headerWritten.load()) { + av_write_trailer(d->m_pFormatContext); + } + int ret = av_interleaved_write_frame(d->m_pFormatContext, nullptr); + if (ret != 0) { + constexpr auto strBufSize = 32; + char strBuf[strBufSize]; + qWarning("%d: Couldn't flush AVFormatContext packet queue: %s", ret, av_make_error_string(strBuf, strBufSize, ret)); + } + avio_flush(d->m_pFormatContext->pb); + avio_closep(&d->m_pIOContext); + avformat_free_context(d->m_pFormatContext); + } + } + + } + + void Muxer::start(IPacketSource *source) { + Q_UNUSED(source) + Q_D(AVQt::Muxer); + + bool shouldBe = false; + if (d->m_running.compare_exchange_strong(shouldBe, true)) { + shouldBe = false; +// if (d->m_headerWritten.compare_exchange_strong(shouldBe, true)) { +// +// } + d->m_paused.store(false); + QThread::start(); + started(); + } + } + + void Muxer::stop(IPacketSource *source) { + Q_UNUSED(source) + Q_D(AVQt::Muxer); + + bool shouldBe = true; + if (d->m_running.compare_exchange_strong(shouldBe, false)) { + d->m_paused.store(false); + wait(); + { + QMutexLocker lock{&d->m_inputQueueMutex}; + while (!d->m_inputQueue.isEmpty()) { + auto packet = d->m_inputQueue.dequeue(); + av_packet_free(&packet.first); + } + } + stopped(); + } + } + + void Muxer::pause(bool p) { + Q_D(AVQt::Muxer); + + bool shouldBe = !p; + if (d->m_paused.compare_exchange_strong(shouldBe, p)) { + paused(p); + } + } + + void Muxer::onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) { + Q_D(AVQt::Muxer); + + bool unknownSource = !d->m_sourceStreamMap.contains(source); + bool initStream = false; + AVStream *addStream; + if (!unknownSource) { + for (const auto &stream : d->m_sourceStreamMap[source].keys()) { + if ((packetType == IPacketSource::CB_VIDEO && stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) || + (packetType == IPacketSource::CB_AUDIO && stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) || + (packetType == IPacketSource::CB_SUBTITLE && stream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)) { + addStream = stream; + initStream = true; + break; + } + } + } + if (unknownSource || !initStream) { + qWarning("[AVQt::Muxer] onPacket() called without preceding call to init() for stream type. Ignoring packet"); + return; + } + + QPair<AVPacket *, AVStream *> queuePacket{av_packet_clone(packet), addStream}; + queuePacket.first->stream_index = addStream->index; + qDebug("[AVQt::Muxer] Getting packet with PTS: %lld", static_cast<long long>(packet->pts)); + av_packet_rescale_ts(queuePacket.first, d->m_sourceStreamMap[source][addStream], addStream->time_base); + + QMutexLocker lock(&d->m_inputQueueMutex); + d->m_inputQueue.enqueue(queuePacket); + } + + void Muxer::run() { + Q_D(AVQt::Muxer); + + while (d->m_running.load()) { + if (!d->m_paused.load() && !d->m_inputQueue.isEmpty()) { + QPair<AVPacket *, AVStream *> packet; + { + QMutexLocker lock(&d->m_inputQueueMutex); + packet = d->m_inputQueue.dequeue(); + } +// av_packet_rescale_ts(packet.first, packet.second->time_base, av_make_q(1, AV_TIME_BASE)); + int ret = av_interleaved_write_frame(d->m_pFormatContext, packet.first); + qDebug("Written packet"); + if (ret != 0) { + constexpr auto strBufSize = 32; + char strBuf[strBufSize]; + qWarning("%d: Couldn't write packet to AVFormatContext: %s", ret, av_make_error_string(strBuf, strBufSize, ret)); + } + } else { + msleep(4); + } + } + } + + int MuxerPrivate::writeToIO(void *opaque, uint8_t *buf, int buf_size) { + auto *outputDevice = reinterpret_cast<QIODevice *>(opaque); + + auto bytesWritten = outputDevice->write(reinterpret_cast<const char *>(buf), buf_size); + return bytesWritten == 0 ? AVERROR_UNKNOWN : static_cast<int>(bytesWritten); + } + + int64_t MuxerPrivate::seekIO(void *opaque, int64_t offset, int whence) { + auto *outputDevice = reinterpret_cast<QIODevice *>(opaque); + + if (outputDevice->isSequential()) { + return AVERROR_UNKNOWN; + } + + bool result; + switch (whence) { + case SEEK_SET: + result = outputDevice->seek(offset); + break; + case SEEK_CUR: + result = outputDevice->seek(outputDevice->pos() + offset); + break; + case SEEK_END: + result = outputDevice->seek(outputDevice->size() - offset); + break; + case AVSEEK_SIZE: + return outputDevice->size(); + default: + return AVERROR_UNKNOWN; + } + + return result ? outputDevice->pos() : AVERROR_UNKNOWN; + } +} \ No newline at end of file diff --git a/AVQt/output/Muxer.h b/AVQt/output/Muxer.h new file mode 100644 index 0000000000000000000000000000000000000000..a0ddeeda02cdf1b994ed35483680c2924d0fb902 --- /dev/null +++ b/AVQt/output/Muxer.h @@ -0,0 +1,64 @@ +// +// Created by silas on 5/24/21. +// + +#include "IPacketSink.h" + +#include <QThread> +#include <QIODevice> + +#ifndef LIBAVQT_MUXER_H +#define LIBAVQT_MUXER_H + +namespace AVQt { + class MuxerPrivate; + + class Muxer : public QThread, public IPacketSink { + Q_OBJECT + Q_INTERFACES(AVQt::IPacketSink) + + Q_DECLARE_PRIVATE(AVQt::Muxer) + + protected: + void run() Q_DECL_OVERRIDE; + + public: + explicit Muxer(QIODevice *outputDevice, QObject *parent = nullptr); + + Muxer(Muxer &) = delete; + + Muxer(Muxer &&other) noexcept; + + bool isPaused() Q_DECL_OVERRIDE; + + void init(IPacketSource *source, AVRational framerate, AVRational timebase, int64_t duration, AVCodecParameters *vParams, + AVCodecParameters *aParams, AVCodecParameters *sParams) Q_DECL_OVERRIDE; + + void deinit(IPacketSource *source) Q_DECL_OVERRIDE; + + void start(IPacketSource *source) Q_DECL_OVERRIDE; + + void stop(IPacketSource *source) Q_DECL_OVERRIDE; + + void pause(bool p) Q_DECL_OVERRIDE; + + void onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) Q_DECL_OVERRIDE; + + void operator=(const Muxer &) = delete; + + signals: + + void started() Q_DECL_OVERRIDE; + + void stopped() Q_DECL_OVERRIDE; + + void paused(bool pause) Q_DECL_OVERRIDE; + + protected: + [[maybe_unused]] explicit Muxer(MuxerPrivate &p); + + MuxerPrivate *d_ptr; + }; +} + +#endif //LIBAVQT_MUXER_H diff --git a/AVQt/output/OpenALAudioOutput.cpp b/AVQt/output/OpenALAudioOutput.cpp index 93afef3b12b3258921e92867470fb8fa61343055..bd6d57881a48a09ec569c2a57ac03e5e6f73fc75 100644 --- a/AVQt/output/OpenALAudioOutput.cpp +++ b/AVQt/output/OpenALAudioOutput.cpp @@ -8,18 +8,16 @@ #include "filter/private/OpenALErrorHandler.h" #include "OpenGLRenderer.h" -#include <QtCore> -#include <QtConcurrent> - namespace AVQt { OpenALAudioOutput::OpenALAudioOutput(QObject *parent) : QThread(parent), d_ptr(new OpenALAudioOutputPrivate(this)) { - Q_D(AVQt::OpenALAudioOutput); - } [[maybe_unused]] OpenALAudioOutput::OpenALAudioOutput(OpenALAudioOutputPrivate &p) : d_ptr(&p) { - Q_D(AVQt::OpenALAudioOutput); + } + OpenALAudioOutput::OpenALAudioOutput(OpenALAudioOutput &&other) noexcept: d_ptr(other.d_ptr) { + other.d_ptr = nullptr; + d_ptr->q_ptr = this; } int OpenALAudioOutput::init(IAudioSource *source, int64_t duration, int sampleRate) { @@ -54,7 +52,7 @@ namespace AVQt { qFatal("Could not make ALC context current"); } - d->m_ALBufferCount = static_cast<int>(std::ceil((1000 * 1.0 / 60) * static_cast<double>(d->m_sampleRate / 1536))); + d->m_ALBufferCount = static_cast<int>(std::ceil((1000 * 1.0 / 60) * (static_cast<double>(d->m_sampleRate) / 1536.0))); qDebug("Using %d OpenAL buffers", d->m_ALBufferCount); d->m_ALBuffers.resize(d->m_ALBufferCount); @@ -62,10 +60,10 @@ namespace AVQt { alCall(alGenBuffers, d->m_ALBufferCount, d->m_ALBuffers.data()); alCall(alGenSources, 1, &d->m_ALSource); - alCall(alSourcef, d->m_ALSource, AL_PITCH, 1); + alCall(alSourcef, d->m_ALSource, AL_PITCH, 1.0f); alCall(alSourcef, d->m_ALSource, AL_GAIN, 1.0f); - alCall(alSource3f, d->m_ALSource, AL_POSITION, 0, 0, 0); - alCall(alSource3f, d->m_ALSource, AL_VELOCITY, 0, 0, 0); + alCall(alSource3f, d->m_ALSource, AL_POSITION, 0.f, 0.f, 0.f); + alCall(alSource3f, d->m_ALSource, AL_VELOCITY, 0.f, 0.f, 0.f); alCall(alSourcei, d->m_ALSource, AL_LOOPING, AL_FALSE); alCall(alSourcei, d->m_ALSource, AL_BUFFER, 0); @@ -114,7 +112,7 @@ namespace AVQt { alCall(alDeleteSources, 1, &d->m_ALSource); alCall(alDeleteBuffers, d->m_ALBufferCount, d->m_ALBuffers.data()); d->m_ALBuffers.clear(); - d->m_ALBuffers.shrink_to_fit(); + d->m_ALBuffers.squeeze(); alcCall(alcMakeContextCurrent, contextCurrent, d->m_ALCDevice, nullptr); alcCall(alcDestroyContext, d->m_ALCDevice, d->m_ALCContext); @@ -192,7 +190,7 @@ namespace AVQt { // pause(nullptr, true); wait(); - qDebug("OpenALAudioOutput at %#lx stopped", reinterpret_cast<int64_t>(this)); + qDebug("OpenALAudioOutput at %#llx stopped", static_cast<long long>(reinterpret_cast<int64_t>(this))); stopped(); @@ -244,8 +242,10 @@ namespace AVQt { { QMutexLocker lock(&d->m_swrContextMutex); if (!d->m_pSwrContext && frame->format != AV_SAMPLE_FMT_S16) { - d->m_pSwrContext = swr_alloc_set_opts(nullptr, frame->channel_layout, AV_SAMPLE_FMT_S16, frame->sample_rate, - frame->channel_layout, static_cast<AVSampleFormat>(frame->format), frame->sample_rate, + d->m_pSwrContext = swr_alloc_set_opts(nullptr, static_cast<int64_t>(frame->channel_layout), AV_SAMPLE_FMT_S16, + frame->sample_rate, + static_cast<int64_t>(frame->channel_layout), + static_cast<AVSampleFormat>(frame->format), frame->sample_rate, 0, nullptr); swr_init(d->m_pSwrContext); } @@ -307,16 +307,17 @@ namespace AVQt { d->m_clock.store(renderer->getClock()); clockIntervalChanged(d->m_clock.load()->getInterval()); - int newBufferCount = static_cast<int>(std::ceil((1000 * 1.0 / static_cast<double>(d->m_clockInterval) * static_cast<double>(d->m_sampleRate) / 1536))); + int newBufferCount = static_cast<int>(std::ceil( + (1000 * 1.0 / static_cast<double>(d->m_clockInterval) * static_cast<double>(d->m_sampleRate) / 1536))); if (d->m_ALBuffers.size() != newBufferCount) { ALCboolean contextCurrent = ALC_FALSE; alcCall(alcMakeContextCurrent, contextCurrent, d->m_ALCDevice, d->m_ALCContext); if (d->m_ALBuffers.size() > newBufferCount) { - auto overhead = d->m_ALBuffers.size() - newBufferCount; + int overhead = static_cast<int>(d->m_ALBuffers.size() - newBufferCount); alCall(alDeleteBuffers, overhead, d->m_ALBuffers.data() + d->m_ALBuffers.size() - overhead); d->m_ALBuffers.resize(newBufferCount); } else if (d->m_ALBuffers.size() < newBufferCount) { - auto extraBuffers = newBufferCount - d->m_ALBuffers.size(); + int extraBuffers = static_cast<int>(newBufferCount - d->m_ALBuffers.size()); d->m_ALBuffers.resize(newBufferCount); alCall(alGenBuffers, extraBuffers, d->m_ALBuffers.data() + d->m_ALBuffers.size() - extraBuffers); } @@ -369,24 +370,27 @@ namespace AVQt { } if (!d->m_ALBufferQueue.isEmpty()) { QMutexLocker lock3(&d->m_ALBufferQueueMutex); - alCall(alSourceQueueBuffers, d->m_ALSource, std::min(d->m_ALBufferQueue.size(), static_cast<typeof (d->m_ALBufferQueue.size())>(4)), d->m_ALBufferQueue.toVector().data()); - for (const auto &buf: QList(d->m_ALBufferQueue.end() - std::min(d->m_ALBufferQueue.size(), static_cast<typeof (d->m_ALBufferQueue.size())>(4)), d->m_ALBufferQueue.end())) { + alCall(alSourceQueueBuffers, d->m_ALSource, static_cast<int>(qMin(d->m_ALBufferQueue.size(), 4)), + d->m_ALBufferQueue.toVector().data()); + for (const auto &buf: d->m_ALBufferQueue.mid(d->m_ALBufferQueue.size() - static_cast<int>(qMin(d->m_ALBufferQueue.size(), 4)))) { d->m_queuedSamples += d->m_ALBufferSampleMap[buf]; } if (d->m_ALBufferQueue.size() < 4) { - alCall(alSourceQueueBuffers, d->m_ALSource, std::min(buffers.size(), 4 - d->m_ALBufferQueue.size()), buffers.data()); - for (qint64 i = buffers.size() - std::min(buffers.size(), 4 - d->m_ALBufferQueue.size()); i < buffers.size(); ++i) { + alCall(alSourceQueueBuffers, d->m_ALSource, static_cast<int>(qMin(buffers.size(), 4 - d->m_ALBufferQueue.size())), + buffers.data()); + for (qint64 i = static_cast<int>(buffers.size() - qMin(buffers.size(), 4 - d->m_ALBufferQueue.size())); + i < buffers.size(); ++i) { d->m_queuedSamples += d->m_ALBufferSampleMap[buffers[i]]; } - buffers.remove(buffers.size() - std::min(buffers.size(), 4 - d->m_ALBufferQueue.size()), - std::min(buffers.size(), 4 - d->m_ALBufferQueue.size())); + buffers.remove(buffers.size() - qMin(buffers.size(), 4 - d->m_ALBufferQueue.size()), + qMin(buffers.size(), 4 - d->m_ALBufferQueue.size())); d->m_ALBufferQueue.append(buffers.toList()); } d->m_ALBufferQueue.clear(); } else { QMutexLocker lock3(&d->m_ALBufferQueueMutex); - alCall(alSourceQueueBuffers, d->m_ALSource, std::min(buffers.size(), static_cast<typeof (d->m_ALBufferQueue.size())>(4)), buffers.data()); - buffers.remove(buffers.size() - std::min(buffers.size(), static_cast<typeof (d->m_ALBufferQueue.size())>(4)), std::min(buffers.size(), static_cast<typeof (d->m_ALBufferQueue.size())>(4))); + alCall(alSourceQueueBuffers, d->m_ALSource, static_cast<int>(qMin(buffers.size(), 4)), buffers.data()); + buffers.remove(buffers.size() - qMin(buffers.size(), 4), qMin(buffers.size(), 4)); d->m_ALBufferQueue.append(buffers.toList()); } } else { @@ -433,7 +437,7 @@ namespace AVQt { if (d->m_inputQueue.isEmpty() || d->m_paused.load()) { return; } - qDebug("Size of output queue: %lld", d->m_inputQueue.size()); + qDebug("Size of output queue: %d", d->m_inputQueue.size()); QMutexLocker lock(&d->m_ALBufferQueueMutex); if (!d->m_ALBufferQueue.isEmpty()) { lock.unlock(); @@ -454,14 +458,15 @@ namespace AVQt { break; } frame = d->m_inputQueue.dequeue(); - qDebug("Discarding audio frame at PTS: %ld < PTS: %lld", frame.first->pts, timestamp); + qDebug("Discarding audio frame at PTS: %lld < PTS: %lld", static_cast<long long>(frame.first->pts), timestamp); av_frame_unref(frame.first); av_frame_free(&frame.first); } } while (!d->m_inputQueue.isEmpty() && !d->m_ALBufferQueue.isEmpty()) { - if (d->m_queuedSamples * 1.0 / d->m_inputQueue.front().first->sample_rate < d->m_clockInterval * 1.5) { + if (static_cast<double>(d->m_queuedSamples) * 1.0 / d->m_inputQueue.front().first->sample_rate < + static_cast<double>(d->m_clockInterval) * 1.5) { lock.relock(); QMutexLocker lock1(&d->m_inputQueueMutex); QMutexLocker lock2(&d->m_ALBufferSampleMapMutex); @@ -482,7 +487,7 @@ namespace AVQt { // } else { frame = d->m_inputQueue.dequeue(); // } - duration += frame.first->nb_samples * 1000.0 / frame.first->sample_rate; + duration += static_cast<int64_t>(std::round(frame.first->nb_samples * 1000.0 / frame.first->sample_rate)); lock1.unlock(); auto buf = d->m_ALBufferQueue.dequeue(); lock.unlock(); diff --git a/AVQt/output/OpenALAudioOutput.h b/AVQt/output/OpenALAudioOutput.h index 476bcf076196d722070f2b05d96f04f3482e2593..2e05a19dbfce87d2e65f7b3e9e1626adfafc9afd 100644 --- a/AVQt/output/OpenALAudioOutput.h +++ b/AVQt/output/OpenALAudioOutput.h @@ -24,6 +24,12 @@ namespace AVQt { public: explicit OpenALAudioOutput(QObject *parent = nullptr); + OpenALAudioOutput(OpenALAudioOutput &&other) noexcept; + + OpenALAudioOutput(const OpenALAudioOutput &) = delete; + + void operator=(const OpenALAudioOutput &) = delete; + Q_INVOKABLE bool isPaused() Q_DECL_OVERRIDE; void run() Q_DECL_OVERRIDE; @@ -66,4 +72,4 @@ namespace AVQt { } -#endif //LIBAVQT_OPENALAUDIOOUTPUT_H +#endif //LIBAVQT_OPENALAUDIOOUTPUT_H \ No newline at end of file diff --git a/AVQt/output/OpenGLRenderer.cpp b/AVQt/output/OpenGLRenderer.cpp index 1a696ba9f72d46779734620cd1ea6eaad4e46d88..8ce839be0e36325e412df022e94d4ed8f2a58982 100644 --- a/AVQt/output/OpenGLRenderer.cpp +++ b/AVQt/output/OpenGLRenderer.cpp @@ -6,7 +6,6 @@ #include "OpenGLRenderer.h" #include <QtGui> -#include <iostream> extern "C" { #include <libavutil/frame.h> @@ -27,9 +26,12 @@ namespace AVQt { [[maybe_unused]] [[maybe_unused]] OpenGLRenderer::OpenGLRenderer(OpenGLRendererPrivate &p) : d_ptr(&p) { } - OpenGLRenderer::~OpenGLRenderer() noexcept { - Q_D(AVQt::OpenGLRenderer); + OpenGLRenderer::OpenGLRenderer(OpenGLRenderer &&other) noexcept: d_ptr(other.d_ptr) { + other.d_ptr = nullptr; + d_ptr->q_ptr = this; + } + OpenGLRenderer::~OpenGLRenderer() noexcept { delete d_ptr; } @@ -37,14 +39,9 @@ namespace AVQt { Q_UNUSED(framerate) // Don't use framerate, because it is not always specified in stream information Q_UNUSED(source) Q_D(AVQt::OpenGLRenderer); - int64_t h = duration / 3600000; - int64_t m = (duration - h * 3600000) / 60000; - int64_t s = (duration - h * 3600000 - m * 60000) / 1000; - int64_t ms = duration - h * 3600000 - m * 60000 - s * 1000; - d->m_duration = QTime(static_cast<int>(h), static_cast<int>(m), static_cast<int>(s), static_cast<int>(ms)); - d->m_position = QTime(0, 0, 0, 0); - qDebug() << "Duration:" << d->m_duration.toString("hh:mm:ss"); + d->m_duration = OpenGLRendererPrivate::timeFromMillis(duration); + qDebug() << "Duration:" << d->m_duration.toString("hh:mm:ss.zzz"); d->m_clock = new RenderClock; d->m_clock->setInterval(16); @@ -104,20 +101,18 @@ namespace AVQt { d->m_vbo.destroy(); d->m_vao.destroy(); - if (d->m_yTexture) { // d->m_yTexture->destroy(); - delete d->m_yTexture; - } + delete d->m_yTexture; + - if (d->m_uTexture) { // d->m_uTexture->destroy(); - delete d->m_uTexture; - } + delete d->m_uTexture; + + - if (d->m_vTexture) { // d->m_yTexture->destroy(); - delete d->m_vTexture; - } + delete d->m_vTexture; + stopped(); return 0; @@ -192,15 +187,26 @@ namespace AVQt { loadResources(); - d->m_program = new QOpenGLShaderProgram(); + QByteArray shaderVersionString; - if (!d->m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/texture.vsh")) { - qDebug() << "Vertex shader errors:\n" << d->m_program->log(); + if (context()->isOpenGLES()) { + shaderVersionString = "#version 300 es\n"; + } else { + shaderVersionString = "#version 330 core\n"; } - if (!d->m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/texture.fsh")) { - qDebug() << "Fragment shader errors:\n" << d->m_program->log(); - } + QFile vsh{":/shaders/texture.vsh"}; + QFile fsh{":/shaders/texture.fsh"}; + vsh.open(QIODevice::ReadOnly); + fsh.open(QIODevice::ReadOnly); + QByteArray vertexShader = vsh.readAll().prepend(shaderVersionString); + QByteArray fragmentShader = fsh.readAll().prepend(shaderVersionString); + vsh.close(); + fsh.close(); + + d->m_program = new QOpenGLShaderProgram(); + d->m_program->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader); + d->m_program->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader); d->m_program->bindAttributeLocation("vertex", OpenGLRendererPrivate::PROGRAM_VERTEX_ATTRIBUTE); d->m_program->bindAttributeLocation("texCoord", OpenGLRendererPrivate::PROGRAM_TEXCOORD_ATTRIBUTE); @@ -333,7 +339,8 @@ namespace AVQt { if (d->m_renderQueue.first().first->pts >= d->m_updateTimestamp.load()) { break; } else { - qDebug("Discarding video frame at PTS: %ld < PTS: %lld", frame.first->pts, d->m_updateTimestamp.load()); + qDebug("Discarding video frame at PTS: %lld < PTS: %lld", static_cast<long long>(frame.first->pts), + d->m_updateTimestamp.load()); av_frame_unref(frame.first); av_frame_free(&frame.first); } @@ -371,34 +378,89 @@ namespace AVQt { } if (firstFrame) { + bool VTexActive = false, UTexActive = false; + QSize YSize, USize, VSize; + QOpenGLTexture::TextureFormat textureFormatY, textureFormatU, textureFormatV; + QOpenGLTexture::PixelFormat pixelFormatY, pixelFormatU, pixelFormatV; + QOpenGLTexture::PixelType pixelType; + switch (static_cast<AVPixelFormat>(d->m_currentFrame->format)) { + case AV_PIX_FMT_BGRA: + YSize = QSize(d->m_currentFrame->width, d->m_currentFrame->height); + textureFormatY = QOpenGLTexture::RGBA8_UNorm; + pixelFormatY = QOpenGLTexture::BGRA; + pixelType = QOpenGLTexture::UInt8; + break; + case AV_PIX_FMT_YUV420P: + YSize = QSize(d->m_currentFrame->width, d->m_currentFrame->height); + USize = VSize = QSize(d->m_currentFrame->width / 2, d->m_currentFrame->height / 2); + textureFormatY = textureFormatU = textureFormatV = QOpenGLTexture::R8_UNorm; + pixelFormatY = pixelFormatU = pixelFormatV = QOpenGLTexture::Red; + pixelType = QOpenGLTexture::UInt8; + UTexActive = VTexActive = true; + break; + case AV_PIX_FMT_YUV420P10: + YSize = QSize(d->m_currentFrame->width, d->m_currentFrame->height); + USize = VSize = QSize(d->m_currentFrame->width / 2, d->m_currentFrame->height / 2); + textureFormatY = textureFormatU = textureFormatV = QOpenGLTexture::R16_UNorm; + pixelFormatY = pixelFormatU = pixelFormatV = QOpenGLTexture::Red; + pixelType = QOpenGLTexture::UInt16; + UTexActive = VTexActive = true; + break; + case AV_PIX_FMT_NV12: + YSize = QSize(d->m_currentFrame->width, d->m_currentFrame->height); + USize = QSize(d->m_currentFrame->width / 2, d->m_currentFrame->height / 2); + textureFormatY = QOpenGLTexture::R8_UNorm; + textureFormatU = QOpenGLTexture::RG8_UNorm; + pixelFormatY = QOpenGLTexture::Red; + pixelFormatU = QOpenGLTexture::RG; + pixelType = QOpenGLTexture::UInt8; + UTexActive = true; + break; + case AV_PIX_FMT_P010: + YSize = QSize(d->m_currentFrame->width, d->m_currentFrame->height); + USize = QSize(d->m_currentFrame->width / 2, d->m_currentFrame->height / 2); + textureFormatY = QOpenGLTexture::R16_UNorm; + textureFormatU = QOpenGLTexture::RG16_UNorm; + pixelFormatY = QOpenGLTexture::Red; + pixelFormatU = QOpenGLTexture::RG; + pixelType = QOpenGLTexture::UInt16; + UTexActive = true; + break; + default: + qFatal("[AVQt::OpenGLRenderer] Unsupported pixel format"); + break; + } d->m_yTexture = new QOpenGLTexture(QOpenGLTexture::Target2D); - d->m_uTexture = new QOpenGLTexture(QOpenGLTexture::Target2D); - d->m_vTexture = new QOpenGLTexture(QOpenGLTexture::Target2D); - d->m_yTexture->setSize(d->m_currentFrame->width, d->m_currentFrame->height); - d->m_uTexture->setSize(d->m_currentFrame->width / 2, d->m_currentFrame->height / 2); - d->m_vTexture->setSize(d->m_currentFrame->width / 2, d->m_currentFrame->height / 2); - d->m_yTexture->setFormat(QOpenGLTexture::RGBA16_UNorm); - d->m_uTexture->setFormat(QOpenGLTexture::RGBA16_UNorm); - d->m_vTexture->setFormat(QOpenGLTexture::RGBA16_UNorm); - d->m_yTexture->allocateStorage(QOpenGLTexture::Red, QOpenGLTexture::UInt16); - d->m_uTexture->allocateStorage(QOpenGLTexture::RG, QOpenGLTexture::UInt16); - d->m_vTexture->allocateStorage(QOpenGLTexture::Red, QOpenGLTexture::UInt16); -// d->m_uTexture = new QOpenGLTexture( -// QImage(d->m_currentFrame->width / 2, d->m_currentFrame->height / 2, QImage::Format_RGBX64)); -// d->m_vTexture = new QOpenGLTexture( -// QImage(d->m_currentFrame->width / 2, d->m_currentFrame->height / 2, QImage::Format_RGBX64)); - + d->m_yTexture->setSize(YSize.width(), YSize.height()); + d->m_yTexture->setFormat(textureFormatY); d->m_yTexture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear); - d->m_uTexture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear); - d->m_vTexture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear); + d->m_yTexture->allocateStorage(pixelFormatY, pixelType); + if (UTexActive) { + d->m_uTexture = new QOpenGLTexture(QOpenGLTexture::Target2D); + d->m_uTexture->setSize(USize.width(), USize.height()); + d->m_uTexture->setFormat(textureFormatU); + d->m_uTexture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear); + d->m_uTexture->allocateStorage(pixelFormatU, pixelType); + } + if (VTexActive) { + d->m_vTexture = new QOpenGLTexture(QOpenGLTexture::Target2D); + d->m_vTexture->setSize(VSize.width(), VSize.height()); + d->m_vTexture->setFormat(textureFormatV); + d->m_vTexture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear); + d->m_vTexture->allocateStorage(pixelFormatV, pixelType); + } } // qDebug("Frame duration: %ld ms", d->m_currentFrameTimeout); if (differentPixFmt) { d->m_program->bind(); } d->m_yTexture->bind(0); - d->m_uTexture->bind(1); - d->m_vTexture->bind(2); + if (d->m_uTexture) { + d->m_uTexture->bind(1); + } + if (d->m_vTexture) { + d->m_vTexture->bind(2); + } switch (d->m_currentFrame->format) { case AV_PIX_FMT_BGRA: d->m_yTexture->setData(QOpenGLTexture::PixelFormat::BGRA, QOpenGLTexture::UInt8, @@ -461,12 +523,16 @@ namespace AVQt { // } if (d->m_currentFrame) { - qDebug("Drawing frame with PTS: %ld", d->m_currentFrame->pts); + qDebug("Drawing frame with PTS: %lld", static_cast<long long>(d->m_currentFrame->pts)); d->m_program->bind(); if (!d->m_yTexture->isBound(0)) { d->m_yTexture->bind(0); - d->m_uTexture->bind(1); - d->m_vTexture->bind(2); + if (d->m_uTexture) { + d->m_uTexture->bind(1); + } + if (d->m_vTexture) { + d->m_vTexture->bind(2); + } } d->m_vao.bind(); @@ -482,23 +548,27 @@ namespace AVQt { d->m_vbo.release(); d->m_program->release(); d->m_yTexture->release(0); - d->m_uTexture->release(1); - d->m_vTexture->release(2); + if (d->m_uTexture) { + d->m_uTexture->release(1); + } + if (d->m_vTexture) { + d->m_vTexture->release(2); + } } - int height = this->height(); +// int height = this->height(); - GLdouble model[4][4], proj[4][4]; - GLint view[4]; - glGetDoublev(GL_MODELVIEW_MATRIX, &model[0][0]); - glGetDoublev(GL_PROJECTION_MATRIX, &proj[0][0]); - glGetIntegerv(GL_VIEWPORT, &view[0]); - GLdouble textPosX = 0, textPosY = 0, textPosZ = 0; +// GLdouble model[4][4], proj[4][4]; +// GLint view[4]; +// glGetDoublev(GL_MODELVIEW_MATRIX, &model[0][0]); +// glGetDoublev(GL_PROJECTION_MATRIX, &proj[0][0]); +// glGetIntegerv(GL_VIEWPORT, &view[0]); +// GLdouble textPosX = 0, textPosY = 0, textPosZ = 0; - OpenGLRendererPrivate::project(-0.9, 0.8, 0.0, &model[0][0], &proj[0][0], &view[0], - &textPosX, &textPosY, &textPosZ); +// OpenGLRendererPrivate::project(-0.9, 0.8, 0.0, &model[0][0], &proj[0][0], &view[0], +// &textPosX, &textPosY, &textPosZ); - textPosY = height - textPosY; // y is inverted +// textPosY = height - textPosY; // y is inverted QPainter p(this); @@ -510,11 +580,16 @@ namespace AVQt { // qDebug() << "Current timestamp:" << d->m_position.toString("hh:mm:ss.zzz"); - QString overlay(d->m_position.toString("hh:mm:ss") + "/" + d->m_duration.toString("hh:mm:ss")); + QTime position{0, 0, 0, 0}; + if (d->m_currentFrame) { + position = OpenGLRendererPrivate::timeFromMillis(d->m_currentFrame->pts / 1000); + } + + QString overlay(position.toString("hh:mm:ss.zzz") + "/" + d->m_duration.toString("hh:mm:ss.zzz")); QFontMetrics fm(roboto); - p.fillRect(fm.boundingRect(overlay).translated(static_cast<int>(textPosX), static_cast<int>(textPosY)).adjusted(-5, -5, 5, 5), + p.fillRect(fm.boundingRect(overlay).translated(static_cast<int>(20), static_cast<int>(20)).adjusted(-5, -5, 5, 5), QColor(0xFF, 0xFF, 0xFF, 0x48)); - p.drawText(fm.boundingRect(overlay).translated(static_cast<int>(textPosX), static_cast<int>(textPosY)), overlay); + p.drawText(fm.boundingRect(overlay).translated(static_cast<int>(20), static_cast<int>(20)), overlay); p.end(); qDebug() << "Paused:" << (d->m_paused.load() ? "true" : "false"); if (!d->m_paused.load()) { @@ -528,14 +603,6 @@ namespace AVQt { } } - void OpenGLRenderer::triggerUpdate(qint64 timestamp) { - Q_UNUSED(timestamp) - Q_D(AVQt::OpenGLRenderer); - -// d->m_updateRequired.store(true); -// d->m_updateTimestamp.store(timestamp); - } - RenderClock *OpenGLRenderer::getClock() { Q_D(AVQt::OpenGLRenderer); return d->m_clock; @@ -552,7 +619,7 @@ namespace AVQt { transformPoint(out, model, in); transformPoint(in, proj, out); - if (in[3] == 0.0) + if (in[3] < 0.0001) return GL_FALSE; in[0] /= in[3]; @@ -575,4 +642,11 @@ namespace AVQt { #undef M } + QTime OpenGLRendererPrivate::timeFromMillis(int64_t ts) { + int ms = static_cast<int>(ts % 1000); + int s = static_cast<int>((ts / 1000) % 60); + int m = static_cast<int>((ts / 1000 / 60) % 60); + int h = static_cast<int>(ts / 1000 / 60 / 60); + return QTime(h, m, s, ms); + } } \ No newline at end of file diff --git a/AVQt/output/OpenGLRenderer.h b/AVQt/output/OpenGLRenderer.h index 96216b70d6e902930edc78c59b1ddee910841f8e..a872577af3fb93025e832149b43dc68b52218841 100644 --- a/AVQt/output/OpenGLRenderer.h +++ b/AVQt/output/OpenGLRenderer.h @@ -29,6 +29,12 @@ namespace AVQt { public: explicit OpenGLRenderer(QWindow *parent = nullptr); + OpenGLRenderer(OpenGLRenderer &&other) noexcept; + + OpenGLRenderer(const OpenGLRenderer &) = delete; + + void operator=(const OpenGLRenderer &) = delete; + ~OpenGLRenderer() noexcept; bool isPaused() Q_DECL_OVERRIDE; @@ -88,11 +94,6 @@ namespace AVQt { */ Q_INVOKABLE void onFrame(IFrameSource *source, AVFrame *frame, int64_t frameDuration) Q_DECL_OVERRIDE; - /*! - * \private - */ - void triggerUpdate(qint64 timestamp); - signals: void started() Q_DECL_OVERRIDE; @@ -115,4 +116,4 @@ namespace AVQt { } -#endif //LIBAVQT_OPENGLRENDERER_H +#endif //LIBAVQT_OPENGLRENDERER_H \ No newline at end of file diff --git a/AVQt/output/RenderClock.cpp b/AVQt/output/RenderClock.cpp index 723b01d6e8a30b26537ea60df2ca9ce2fe8158fe..2d19d3edea4d93af669e2a22dff2158f7e8a7e06 100644 --- a/AVQt/output/RenderClock.cpp +++ b/AVQt/output/RenderClock.cpp @@ -24,6 +24,11 @@ namespace AVQt { connect(d->m_timer, &QTimer::timeout, this, &RenderClock::timerTimeout); } + RenderClock::RenderClock(RenderClock &&other) noexcept: d_ptr(other.d_ptr) { + other.d_ptr = nullptr; + d_ptr->q_ptr = this; + } + RenderClock::~RenderClock() { Q_D(AVQt::RenderClock); @@ -78,13 +83,13 @@ namespace AVQt { } } - void RenderClock::setInterval(int64_t interval) { + void RenderClock::setInterval(int interval) { Q_D(AVQt::RenderClock); - if (interval > 0 && interval != d->m_interval.load()) { + if (interval > 0 && interval != d->m_interval) { d->m_interval.store(interval); - d->m_timer->setInterval(d->m_interval.load()); + d->m_timer->setInterval(d->m_interval); d->m_timer->stop(); d->m_timer->start(); diff --git a/AVQt/output/RenderClock.h b/AVQt/output/RenderClock.h index 6e1ee39d1ddda44555ea7db5519717fc26aa0c7f..3cb95b65c0d25fa3348e511ad2a1e134365b9360 100644 --- a/AVQt/output/RenderClock.h +++ b/AVQt/output/RenderClock.h @@ -18,9 +18,15 @@ namespace AVQt { public: explicit RenderClock(QObject *parent = nullptr); + RenderClock(RenderClock &&other) noexcept; + + RenderClock(const RenderClock &) = delete; + + void operator=(const RenderClock &) = delete; + ~RenderClock(); - void setInterval(int64_t interval); + void setInterval(int interval); int64_t getInterval(); @@ -55,6 +61,7 @@ namespace AVQt { RenderClockPrivate *d_ptr; private slots: + void timerTimeout(); }; diff --git a/AVQt/output/private/Muxer_p.h b/AVQt/output/private/Muxer_p.h new file mode 100644 index 0000000000000000000000000000000000000000..cbe1523645755427e74bf816403d36950e46039d --- /dev/null +++ b/AVQt/output/private/Muxer_p.h @@ -0,0 +1,53 @@ +// +// Created by silas on 5/24/21. +// + +#include "../Muxer.h" + +extern "C" { +#include <libavformat/avformat.h> +#include <libavcodec/avcodec.h> +} + +#include <QtCore> + +#ifndef LIBAVQT_MUXER_P_H +#define LIBAVQT_MUXER_P_H + +namespace AVQt { + class MuxerPrivate { + public: + MuxerPrivate(const MuxerPrivate &) = delete; + + void operator=(const MuxerPrivate &) = delete; + + private: + explicit MuxerPrivate(Muxer *q) : q_ptr(q) {}; + + Muxer *q_ptr; + + QIODevice *m_outputDevice{nullptr}; + + QMutex m_initMutex{}; + static constexpr size_t IOBUF_SIZE{4 * 1024}; // 4 KB + uint8_t *m_pIOBuffer{nullptr}; + AVIOContext *m_pIOContext{nullptr}; + AVFormatContext *m_pFormatContext{nullptr}; + QMap<IPacketSource *, QMap<AVStream *, AVRational>> m_sourceStreamMap{}; + + QMutex m_inputQueueMutex{}; + QQueue<QPair<AVPacket *, AVStream *>> m_inputQueue{}; + + std::atomic_bool m_running{false}; + std::atomic_bool m_paused{false}; + std::atomic_bool m_headerWritten{false}; + + friend class Muxer; + + static int64_t seekIO(void *opaque, int64_t offset, int whence); + + static int writeToIO(void *opaque, uint8_t *buf, int buf_size); + }; +} + +#endif //LIBAVQT_MUXER_P_H diff --git a/AVQt/output/private/OpenALAudioOutput_p.h b/AVQt/output/private/OpenALAudioOutput_p.h index 04b678b8fdd56cff826e5571793fa7cbf9480a8b..5ebc8169d9ee4f149f6063b12e10ec986828cc4e 100644 --- a/AVQt/output/private/OpenALAudioOutput_p.h +++ b/AVQt/output/private/OpenALAudioOutput_p.h @@ -22,46 +22,52 @@ namespace AVQt { class OpenALAudioOutput; class OpenALAudioOutputPrivate { + public: + OpenALAudioOutputPrivate(const OpenALAudioOutputPrivate &) = delete; + + void operator=(const OpenALAudioOutputPrivate &) = delete; + + private: explicit OpenALAudioOutputPrivate(OpenALAudioOutput *q) : q_ptr(q) {}; - [[maybe_unused]] OpenALAudioOutput *q_ptr; + OpenALAudioOutput *q_ptr; - QMutex m_inputQueueMutex; - QQueue<QPair<AVFrame *, int64_t>> m_inputQueue; + QMutex m_inputQueueMutex{}; + QQueue<QPair<AVFrame *, int64_t>> m_inputQueue{}; // AVFrame *m_partialFrame {nullptr}; // size_t m_partialFrameOffset {0}; - QMutex m_outputQueueMutex; - QQueue<QPair<AVFrame *, int64_t>> m_outputQueue; - std::atomic_bool m_outputSliceDurationChanged {false}; - int64_t m_duration = 0, m_clockInterval = 0, m_sampleRate {0}; - - std::atomic<RenderClock *> m_clock {nullptr}; - - QMutex m_swrContextMutex; - SwrContext *m_pSwrContext {nullptr}; - - int m_ALBufferCount = 5; - std::atomic_size_t m_playingBuffers {0}; - QVector<ALuint> m_ALBuffers; - QRecursiveMutex m_ALBufferQueueMutex; - QQueue<ALuint> m_ALBufferQueue; - QRecursiveMutex m_ALBufferSampleMapMutex; - QMap<ALuint, int64_t> m_ALBufferSampleMap; - int64_t m_queuedSamples {0}; - ALCdevice *m_ALCDevice {nullptr}; - ALCcontext *m_ALCContext {nullptr}; - - ALuint m_ALSource {0}; + QMutex m_outputQueueMutex{}; + QQueue<QPair<AVFrame *, int64_t>> m_outputQueue{}; + std::atomic_bool m_outputSliceDurationChanged{false}; + int64_t m_duration{0}, m_clockInterval{0}, m_sampleRate{0}; + + std::atomic<RenderClock *> m_clock{nullptr}; + + QMutex m_swrContextMutex{}; + SwrContext *m_pSwrContext{nullptr}; + + int m_ALBufferCount{5}; + std::atomic_int64_t m_playingBuffers{0}; + QVector<ALuint> m_ALBuffers{}; + QMutex m_ALBufferQueueMutex{}; + QQueue<ALuint> m_ALBufferQueue{}; + QMutex m_ALBufferSampleMapMutex{}; + QMap<ALuint, int64_t> m_ALBufferSampleMap{}; + int64_t m_queuedSamples{0}; + ALCdevice *m_ALCDevice{nullptr}; + ALCcontext *m_ALCContext{nullptr}; + + ALuint m_ALSource{0}; // Threading stuff - std::atomic_bool m_running {false}; - std::atomic_bool m_paused {false}; + std::atomic_bool m_running{false}; + std::atomic_bool m_paused{false}; - std::atomic_bool m_wasPaused {false}; - std::atomic_int64_t m_lastUpdate {0}; - std::atomic_size_t m_audioFrame {0}; + std::atomic_bool m_wasPaused{false}; + std::atomic_int64_t m_lastUpdate{0}; + std::atomic_size_t m_audioFrame{0}; friend class OpenALAudioOutput; }; } -#endif //LIBAVQT_OPENALAUDIOOUTPUT_P_H \ No newline at end of file +#endif //LIBAVQT_OPENALAUDIOOUTPUT_P_H diff --git a/AVQt/output/private/OpenGLRenderer_p.h b/AVQt/output/private/OpenGLRenderer_p.h index c5a00e6385b256621adcef570ba54c42575366ab..90a33dbb94c47b4376c6b5261452abcb876190b9 100644 --- a/AVQt/output/private/OpenGLRenderer_p.h +++ b/AVQt/output/private/OpenGLRenderer_p.h @@ -23,7 +23,13 @@ namespace AVQt { * \private * \internal */ - class OpenGLRendererPrivate { + class OpenGLRendererPrivate : public QObject { + public: + OpenGLRendererPrivate(const OpenGLRendererPrivate &) = delete; + + void operator=(const OpenGLRendererPrivate &) = delete; + + private: explicit OpenGLRendererPrivate(OpenGLRenderer *q) : q_ptr(q) {}; static GLint @@ -32,30 +38,31 @@ namespace AVQt { static inline void transformPoint(GLdouble out[4], const GLdouble m[16], const GLdouble in[4]); + static QTime timeFromMillis(int64_t ts); - OpenGLRenderer *q_ptr {nullptr}; + OpenGLRenderer *q_ptr{nullptr}; - QMutex m_renderQueueMutex; - QQueue<QPair<AVFrame *, int64_t>> m_renderQueue; + QMutex m_renderQueueMutex{}; + QQueue<QPair<AVFrame *, int64_t>> m_renderQueue{}; - RenderClock *m_clock {nullptr}; - int64_t m_currentFrameTimeout = 1; - QTime m_duration; - QTime m_position; - std::atomic_bool m_updateRequired {true}, m_paused {false}, m_running {false}, m_firstFrame {true}; - std::atomic<qint64> m_updateTimestamp {0}; - std::chrono::time_point<std::chrono::high_resolution_clock> m_lastFrame; + RenderClock *m_clock{nullptr}; + int64_t m_currentFrameTimeout{1}; + QTime m_duration{}; + QTime m_position{}; + std::atomic_bool m_updateRequired{true}, m_paused{false}, m_running{false}, m_firstFrame{true}; + std::atomic<qint64> m_updateTimestamp{0}; + std::chrono::time_point<std::chrono::high_resolution_clock> m_lastFrame{}; - QMutex m_currentFrameMutex; - AVFrame *m_currentFrame {nullptr}; + QMutex m_currentFrameMutex{}; + AVFrame *m_currentFrame{nullptr}; - QOpenGLVertexArrayObject m_vao; - QOpenGLBuffer m_vbo, m_ibo; - QOpenGLShaderProgram *m_program {nullptr}; - QOpenGLTexture *m_yTexture {nullptr}, *m_uTexture {nullptr}, *m_vTexture {nullptr}; + QOpenGLVertexArrayObject m_vao{}; + QOpenGLBuffer m_vbo{}, m_ibo{}; + QOpenGLShaderProgram *m_program{nullptr}; + QOpenGLTexture *m_yTexture{nullptr}, *m_uTexture{nullptr}, *m_vTexture{nullptr}; - static constexpr uint PROGRAM_VERTEX_ATTRIBUTE {0}; - static constexpr uint PROGRAM_TEXCOORD_ATTRIBUTE {1}; + static constexpr uint PROGRAM_VERTEX_ATTRIBUTE{0}; + static constexpr uint PROGRAM_TEXCOORD_ATTRIBUTE{1}; friend class OpenGLRenderer; }; diff --git a/AVQt/output/private/RenderClock_p.h b/AVQt/output/private/RenderClock_p.h index de66bdc9fcb5f6225787723e8556e4dea51de646..71ff96a97a5d9006b64bd2b44718e3b50b7158ed 100644 --- a/AVQt/output/private/RenderClock_p.h +++ b/AVQt/output/private/RenderClock_p.h @@ -9,15 +9,21 @@ namespace AVQt { class RenderClockPrivate { + public: + RenderClockPrivate(const RenderClockPrivate &) = delete; + + void operator=(const RenderClockPrivate &) = delete; + + private: explicit RenderClockPrivate(RenderClock *q) : q_ptr(q) {}; RenderClock *q_ptr; - QTimer *m_timer {nullptr}; - std::atomic_int64_t m_interval {1}; - std::atomic_bool m_active {false}; - std::atomic_bool m_paused {false}; - QElapsedTimer *m_elapsedTime {nullptr}; - qint64 m_lastPauseTimestamp {0}; + QTimer *m_timer{nullptr}; + std::atomic_int m_interval{1}; + std::atomic_bool m_active{false}; + std::atomic_bool m_paused{false}; + QElapsedTimer *m_elapsedTime{nullptr}; + qint64 m_lastPauseTimestamp{0}; friend class RenderClock; }; diff --git a/AVQt/resources.qrc b/AVQt/resources.qrc index d76d75d131fa716b2b0cb26a08398b6267ab9349..dba686671f093444f81869b7722fa628f4a36b7c 100644 --- a/AVQt/resources.qrc +++ b/AVQt/resources.qrc @@ -2,11 +2,11 @@ <qresource prefix="/shaders"> <file>texture.vsh</file> <file>texture.fsh</file> - <file>textureNV12.fsh</file> - <file>textureP010.fsh</file> + <file>textureES.vsh</file> + <file>textureES.fsh</file> </qresource> <qresource prefix="/images"> <file>frame.bmp</file> <file>frame.yuv</file> </qresource> -</RCC> +</RCC> \ No newline at end of file diff --git a/AVQt/texture.fsh b/AVQt/texture.fsh index 43c3f19edf4087503aa078f586f2d1090a80d0f2..6a1fdcd7757aacaa7dd1c9f09b9bd154f8c8aa2b 100644 --- a/AVQt/texture.fsh +++ b/AVQt/texture.fsh @@ -1,7 +1,11 @@ -#version 330 core - // fragment shader +#ifdef GL_ES +// Set default precision to medium +precision mediump int; +precision mediump float; +#endif + in vec4 fragColor;// input: interpolated color as rgba-value in vec2 texCoord;// input: texture coordinate (xy-coordinates) out vec4 finalColor;// output: final color value as rgba-value @@ -15,37 +19,37 @@ void main() { vec2 newTexCoord = vec2(1.0f - texCoord.x, texCoord.y); if (inputFormat == 0) { // RGB(A) - vec3 rgb = texture(textureY, newTexCoord).rgb; + vec3 rgb = texture2D(textureY, newTexCoord).rgb; finalColor = vec4(rgb, 1.0); } else if (inputFormat == 1) { // YUV 4:2:0 with Y plane interleaved UV plane (e.g. NV12 or P010) vec3 yuv; // We had put the Y values of each pixel to the R component by using GL_RED - yuv.x = texture(textureY, newTexCoord.st).r - 0.0625; + yuv.x = texture2D(textureY, newTexCoord.st).r - 0.0625; // We had put the U and V values of each pixel to the R and G components of the // texture respectively using GL_RG. Since U, V bytes are interspread // in the texture, this is probably the fastest way to use them in the shader - yuv.y = texture(textureU, newTexCoord.st).r - 0.5; - yuv.z = texture(textureU, newTexCoord.st).g - 0.5; + yuv.y = texture2D(textureU, newTexCoord.st).r - 0.5; + yuv.z = texture2D(textureU, newTexCoord.st).g - 0.5; // The numbers are just YUV to RGB conversion constants - vec3 rgb = mat3(1, 1, 1, - 0, -0.39465, 2.03211, - 1.13983, -0.58060, 0) * yuv; + vec3 rgb = mat3(1.0, 1.0, 1.0, + 0.0, -0.39465, 2.03211, + 1.13983, -0.58060, 0.0) * yuv; finalColor = vec4(rgb, 1.0f); } else if (inputFormat == 2) { // YUV420P vec3 yuv; // We had put the Y values of each pixel to the R component by using GL_RED - yuv.x = texture(textureY, newTexCoord.st).r - 0.0625; + yuv.x = texture2D(textureY, newTexCoord.st).r - 0.0625; // We had put the U and V values of each pixel to the R and G components of the // texture respectively using GL_RG. Since U, V bytes are interspread // in the texture, this is probably the fastest way to use them in the shader - yuv.y = texture(textureU, newTexCoord.st).r - 0.5; - yuv.z = texture(textureV, newTexCoord.st).r - 0.5; + yuv.y = texture2D(textureU, newTexCoord.st).r - 0.5; + yuv.z = texture2D(textureV, newTexCoord.st).r - 0.5; // The numbers are just YUV to RGB conversion constants - vec3 rgb = mat3(1, 1, 1, - 0, -0.39465, 2.03211, - 1.13983, -0.58060, 0) * yuv; + vec3 rgb = mat3(1.0, 1.0, 1.0, + 0.0, -0.39465, 2.03211, + 1.13983, -0.58060, 0.0) * yuv; finalColor = vec4(rgb, 1.0f); } else if (inputFormat == 3) { // YUV420P10 vec3 yuv; @@ -53,13 +57,13 @@ void main() { // Multiply every value with 2^6 = 64, because the values are stored in the lower 10 bits, // but have to be in the upper 10 bits for correct calculation // We had put the Y values of each pixel to the R component of every texture by using GL_RED - yuv.x = texture(textureY, newTexCoord.st).r * 64 - 0.0625; - yuv.y = texture(textureU, newTexCoord.st).r * 64 - 0.5; - yuv.z = texture(textureV, newTexCoord.st).r * 64 - 0.5; + yuv.x = texture2D(textureY, newTexCoord.st).r * 64.0 - 0.0625; + yuv.y = texture2D(textureU, newTexCoord.st).r * 64.0 - 0.5; + yuv.z = texture2D(textureV, newTexCoord.st).r * 64.0 - 0.5; // The numbers are just YUV to RGB conversion constants - vec3 rgb = mat3(1, 1, 1, - 0, -0.39465, 2.03211, - 1.13983, -0.58060, 0) * yuv; + vec3 rgb = mat3(1.0, 1.0, 1.0, + 0.0, -0.39465, 2.03211, + 1.13983, -0.58060, 0.0) * yuv; finalColor = vec4(rgb, 1.0); } diff --git a/AVQt/texture.vsh b/AVQt/texture.vsh index 1bd6500697ee58eab971e4852131da04cb1739ee..83caa39e01b930166a2641b00198de9457550ed2 100644 --- a/AVQt/texture.vsh +++ b/AVQt/texture.vsh @@ -1,15 +1,18 @@ -#version 330 - -// GLSL version 3.3 // vertex shader -layout(location = 0) in vec3 position; // input: attribute with index '0' with 3 elements per vertex +#ifdef GL_ES +// Set default precision to medium +precision mediump int; +precision mediump float; +#endif + +layout(location = 0) in vec3 position;// input: attribute with index '0' with 3 elements per vertex //layout(location = 1) in vec3 color; // input: attribute with index '1' with 3 elements (=rgb) per vertex -layout(location = 1) in vec2 texcoords; // input: attribute with index '2' with 2 elements per vertex +layout(location = 1) in vec2 texcoords;// input: attribute with index '2' with 2 elements per vertex //layout(location = 2) in float texnr; // input: attribute with index '3' with 1 float per vertex -out vec4 fragColor; // output: computed fragmentation color -out vec2 texCoord; // output: computed texture coordinates +out vec4 fragColor;// output: computed fragmentation color +out vec2 texCoord;// output: computed texture coordinates //flat out float texID; // output: texture ID - mind the 'flat' attribute! void main() { @@ -18,4 +21,4 @@ void main() { fragColor = vec4(0.5, 1.0, 0.3, 1.0); texCoord = texcoords; // texID = texnr; -} +} \ No newline at end of file diff --git a/AVQt/textureES.fsh b/AVQt/textureES.fsh new file mode 100644 index 0000000000000000000000000000000000000000..83a502131e21411c2bafd935c6e0b2dbc2035912 --- /dev/null +++ b/AVQt/textureES.fsh @@ -0,0 +1,62 @@ +#version 300 es + +// fragment shader + +in mediump vec4 fragColor;// input: interpolated color as rgba-value +in mediump vec2 texCoord;// input: texture coordinate (xy-coordinates) +out mediump vec4 finalColor;// output: final color value as rgba-value + +uniform sampler2D textureY; +uniform sampler2D textureU; +uniform sampler2D textureV; +uniform int inputFormat; + +void main() { + mediump vec2 newTexCoord = vec2(1.0f - texCoord.x, texCoord.y); + + if (inputFormat == 0) { // RGB(A) + mediump vec3 rgb = texture(textureY, newTexCoord).rgb; + finalColor = vec4(rgb, 1.0); + } else if (inputFormat == 1) { // YUV 4:2:0 with Y plane and interleaved UV plane (e.g. NV12 or P010) + mediump vec3 yuv; + + // We had put the Y values of each pixel to the R component by using GL_RED + yuv.x = texture(textureY, newTexCoord.st).r - 0.0625; + // We had put the U and V values of each pixel to the R and G components of the + // texture respectively using GL_RG. Since U, V bytes are interspread + // in the texture, this is probably the fastest way to use them in the shader + yuv.y = texture(textureU, newTexCoord.st).r - 0.5; + yuv.z = texture(textureU, newTexCoord.st).g - 0.5; + // The numbers are just YUV to RGB conversion constants + mediump vec3 rgb = mat3(1.0, 1.0, 1.0, + 0.0, -0.39465, 2.03211, + 1.13983, -0.58060, 0.0) * yuv; + finalColor = vec4(rgb, 1.0f); + } else if (inputFormat == 2) { // YUV420P + mediump vec3 yuv; + yuv.x = texture(textureY, newTexCoord.st).r * 4.0; + // yuv.x = texture(textureY, newTexCoord.st).r - 0.0625; + yuv.y = texture(textureU, newTexCoord.st).r - 0.5; + yuv.z = texture(textureV, newTexCoord.st).r - 0.5; + // The numbers are just YUV to RGB conversion constants + // mediump vec3 rgb = mat3(1.0, 1.0, 1.0, + // 0.0, -0.39465, 2.03211, + // 1.13983, -0.58060, 0.0) * yuv; + finalColor = vec4(yuv.x, yuv.x, yuv.x, 1.0f); + } else if (inputFormat == 3) { // YUV420P10 + mediump vec3 yuv; + + // Multiply every value with 2^6 = 64, because the values are stored in the lower 10 bits, + // but have to be in the upper 10 bits for correct calculation + // We had put the Y values of each pixel to the R component of every texture by using GL_RED + yuv.x = texture(textureY, newTexCoord.st).r * 64.0 - 0.0625; + yuv.y = texture(textureU, newTexCoord.st).r * 64.0 - 0.5; + yuv.z = texture(textureV, newTexCoord.st).r * 64.0 - 0.5; + // The numbers are just YUV to RGB conversion constants + mediump vec3 rgb = mat3(1.0, 1.0, 1.0, + 0.0, -0.39465, 2.03211, + 1.13983, -0.58060, 0.0) * yuv; + + finalColor = vec4(rgb, 1.0); + } +} \ No newline at end of file diff --git a/AVQt/textureES.vsh b/AVQt/textureES.vsh new file mode 100644 index 0000000000000000000000000000000000000000..936f2a993a50d8536251fd4da89886db19c180a3 --- /dev/null +++ b/AVQt/textureES.vsh @@ -0,0 +1,16 @@ +#version 300 es + +// GLSL version 3.3 +// vertex shader + +layout(location = 0) in mediump vec3 position;// input: attribute with index '0' with 3 elements per vertex +layout(location = 1) in mediump vec2 texcoords;// input: attribute with index '2' with 2 elements per vertex + +out mediump vec4 fragColor;// output: computed fragmentation color +out mediump vec2 texCoord;// output: computed texture coordinates + +void main() { + gl_Position = vec4(position, 1.0); + fragColor = vec4(0.5, 1.0, 0.3, 1.0); + texCoord = texcoords; +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e3ff5081ca97ee4dba87b198e3c9fca5db601de..9a4414e8199c6c64be30ee0a20db1450da0d4bd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.16) project(LibAVQt) set(CMAKE_CXX_STANDARD 20) @@ -9,4 +9,4 @@ set(CMAKE_AUTOUIC on) add_subdirectory(AVQt) include_directories(AVQt) -add_subdirectory(Player) \ No newline at end of file +add_subdirectory(Player) diff --git a/Player.pro.user b/Player.pro.user index be6e13f22ce1231dee0e3945ef97de4c1140f736..f83227fa715eaf8098ae427188c8cc4f03cc6810 100644 --- a/Player.pro.user +++ b/Player.pro.user @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE QtCreatorProject> -<!-- Written by QtCreator 4.14.1, 2021-05-08T11:26:40. --> +<!-- Written by QtCreator 4.14.1, 2021-05-28T17:40:00. --> <qtcreator> <data> <variable>EnvironmentId</variable> @@ -8,7 +8,7 @@ </data> <data> <variable>ProjectExplorer.Project.ActiveTarget</variable> - <value type="int">0</value> + <value type="int">1</value> </data> <data> <variable>ProjectExplorer.Project.EditorSettings</variable> @@ -109,8 +109,8 @@ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value> </valuemap> <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value> </valuemap> <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1"> @@ -120,8 +120,8 @@ <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value> </valuemap> <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value> </valuemap> <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value> @@ -151,8 +151,8 @@ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value> </valuemap> <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value> </valuemap> <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1"> @@ -162,8 +162,8 @@ <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value> </valuemap> <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value> </valuemap> <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value> @@ -193,8 +193,8 @@ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value> </valuemap> <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value> </valuemap> <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1"> @@ -204,8 +204,8 @@ <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value> </valuemap> <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value> </valuemap> <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value> @@ -223,8 +223,8 @@ <valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0"> <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0"> <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value> </valuemap> <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value> @@ -311,15 +311,16 @@ <variable>ProjectExplorer.Project.Target.1</variable> <valuemap type="QVariantMap"> <value type="QString" key="DeviceType">Desktop</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">MinGW x86_64</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">MinGW x86_64</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{d1a101cb-333a-40d5-afd0-8c544cd86c51}</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{ff2c954c-33d9-44cd-91b8-591dd542e6e9}</value> <value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value> <value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value> <value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value> <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0"> - <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/silas/CLionProjects/build-Player-MinGW_x86_64-Debug</value> - <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/silas/CLionProjects/build-Player-MinGW_x86_64-Debug</value> + <value type="int" key="EnableQmlDebugging">0</value> + <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/silas/CLionProjects/build-Player-Desktop-Debug</value> + <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/silas/CLionProjects/build-Player-Desktop-Debug</value> <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0"> <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0"> <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value> @@ -333,8 +334,8 @@ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value> </valuemap> <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value> </valuemap> <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1"> @@ -344,8 +345,8 @@ <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value> </valuemap> <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value> </valuemap> <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value> @@ -355,12 +356,12 @@ <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value> <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value> - <value type="int" key="QtQuickCompiler">0</value> + <value type="int" key="RunSystemFunction">0</value> </valuemap> <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1"> <value type="int" key="EnableQmlDebugging">0</value> - <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/silas/CLionProjects/build-Player-MinGW_x86_64-Release</value> - <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/silas/CLionProjects/build-Player-MinGW_x86_64-Release</value> + <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/silas/CLionProjects/build-Player-Desktop-Release</value> + <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/silas/CLionProjects/build-Player-Desktop-Release</value> <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0"> <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0"> <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value> @@ -374,8 +375,8 @@ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value> </valuemap> <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value> </valuemap> <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1"> @@ -385,8 +386,8 @@ <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value> </valuemap> <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value> </valuemap> <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value> @@ -401,8 +402,8 @@ </valuemap> <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2"> <value type="int" key="EnableQmlDebugging">0</value> - <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/silas/CLionProjects/build-Player-MinGW_x86_64-Profile</value> - <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/silas/CLionProjects/build-Player-MinGW_x86_64-Profile</value> + <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/silas/CLionProjects/build-Player-Desktop-Profile</value> + <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/silas/CLionProjects/build-Player-Desktop-Profile</value> <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0"> <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0"> <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value> @@ -416,8 +417,8 @@ <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value> </valuemap> <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value> </valuemap> <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1"> @@ -427,8 +428,8 @@ <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value> </valuemap> <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value> </valuemap> <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value> @@ -446,8 +447,8 @@ <valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0"> <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0"> <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value> - <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value> + <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value> <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value> </valuemap> <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value> @@ -524,7 +525,7 @@ <value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value> <value type="bool" key="RunConfiguration.UseQmlDebugger">false</value> <value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value> - <value type="bool" key="RunConfiguration.UseTerminal">true</value> + <value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/silas/CLionProjects/build-Player-Desktop-Debug/Player</value> </valuemap> <value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value> </valuemap> diff --git a/Player/CMakeLists.txt b/Player/CMakeLists.txt index 684765b0169160633af05a4c1670e2cfd912d249..7b5e0e2d7235b717ebc42b1edd318b4aa02de322 100644 --- a/Player/CMakeLists.txt +++ b/Player/CMakeLists.txt @@ -1,19 +1,22 @@ #SET(CMAKE_INSTALL_RPATH "$ORIGIN/../AVQt") #SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH on) -find_package(Qt6 COMPONENTS Core Gui Widgets OpenGL OpenGLWidgets Concurrent) -#find_package(Qt5 COMPONENTS Core Gui Widgets OpenGL Concurrent) +#find_package(Qt6 COMPONENTS Core Gui Widgets OpenGL OpenGLWidgets Concurrent) + +set(CMAKE_CXX_STANDARD 20) + +find_package(Qt5 COMPONENTS Core Gui Widgets OpenGL Concurrent) find_package(OpenAL) #add_compile_options(-O4) add_executable(Player main.cpp) -target_link_libraries(Player Qt6::Core Qt6::Gui Qt6::Concurrent Qt6::Widgets Qt6::OpenGLWidgets Qt6::OpenGL avformat avfilter avutil avcodec avdevice swscale swresample AVQt) -#target_link_libraries(Player Qt5::Core Qt5::Gui Qt5::Concurrent Qt5::Widgets Qt5::OpenGL avformat avfilter avutil avcodec avdevice swscale swresample AVQt) +#target_link_libraries(Player Qt6::Core Qt6::Gui Qt6::Concurrent Qt6::Widgets Qt6::OpenGLWidgets Qt6::OpenGL avformat avfilter avutil avcodec avdevice swscale swresample AVQt) +target_link_libraries(Player Qt5::Core Qt5::Gui Qt5::Concurrent Qt5::Widgets Qt5::OpenGL avformat avfilter avutil avcodec avdevice swscale swresample AVQt) if (LINUX) target_link_libraries(Player openal GL) elseif (WIN32) target_link_libraries(Player OpenAL32 opengl32) -endif () \ No newline at end of file +endif () diff --git a/Player/main.cpp b/Player/main.cpp index 5d6937800afc33a297a0c6beef2e75ceb0a0cc18..7a4cef9d521e602b0ce52c88599296c90e85133a 100644 --- a/Player/main.cpp +++ b/Player/main.cpp @@ -4,6 +4,7 @@ #include <QFileDialog> #include <csignal> #include <iostream> +#include <qglobal.h> constexpr auto LOGFILE_LOCATION = "libAVQt.log"; @@ -28,7 +29,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt auto now = QDateTime::currentDateTime(); os << now.toString(Qt::ISODateWithMs) << ": "; - os << qPrintable(qFormatLogMessage(type, context, message)) << Qt::endl; + os << qPrintable(qFormatLogMessage(type, context, message)) << "\n"; std::cerr << output.toStdString(); @@ -59,15 +60,15 @@ int main(int argc, char *argv[]) { inputFile->open(QIODevice::ReadOnly); AVQt::Demuxer demuxer(inputFile); - AVQt::AudioDecoder decoder; - AVQt::OpenALAudioOutput output; +// AVQt::AudioDecoder decoder; +// AVQt::OpenALAudioOutput output; - demuxer.registerCallback(&decoder, AVQt::IPacketSource::CB_AUDIO); - decoder.registerCallback(&output); +// demuxer.registerCallback(&decoder, AVQt::IPacketSource::CB_AUDIO); +// decoder.registerCallback(&output); AVQt::IDecoder *videoDecoder; #ifdef Q_OS_LINUX - videoDecoder = new AVQt::DecoderQSV; + videoDecoder = new AVQt::DecoderVAAPI; #elif defined(Q_OS_WINDOWS) videoDecoder = new AVQt::DecoderDXVA2(); #else @@ -79,6 +80,12 @@ int main(int argc, char *argv[]) { demuxer.registerCallback(videoDecoder, AVQt::IPacketSource::CB_VIDEO); // videoDecoder->registerCallback(encoder); + +// QFile outputFile("output.ts"); +// outputFile.open(QIODevice::ReadWrite | QIODevice::Truncate); +// AVQt::Muxer muxer(&outputFile); + +// encoder->registerCallback(&muxer, AVQt::IPacketSource::CB_VIDEO); videoDecoder->registerCallback(&renderer); renderer.setMinimumSize(QSize(360, 240)); @@ -92,7 +99,7 @@ int main(int argc, char *argv[]) { demuxer.init(); - output.syncToOutput(&renderer); +// output.syncToOutput(&renderer); demuxer.start();