diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000000000000000000000000000000000000..ba0f7e5ea16a2606db664148aefdeaa4437feca7 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,8 @@ +<component name="InspectionProjectProfileManager"> + <profile version="1.0"> + <option name="myName" value="Project Default" /> + <inspection_tool class="Clazy" enabled="true" level="WARNING" enabled_by_default="true"> + <option name="clazyChecks" value="level0,no-qt-macros" /> + </inspection_tool> + </profile> +</component> \ No newline at end of file diff --git a/AVQt/filter/DecoderVAAPI.cpp b/AVQt/filter/DecoderVAAPI.cpp index a68f3edf229d6faf9bf99d846ac56483e12743b3..d61dcecebc86002e501cdd314dbc8c3e104ba7d5 100644 --- a/AVQt/filter/DecoderVAAPI.cpp +++ b/AVQt/filter/DecoderVAAPI.cpp @@ -72,6 +72,13 @@ namespace AVQt { stop(); + { + QMutexLocker lock(&d->m_cbListMutex); + for (const auto &cb: d->m_cbList) { + cb->deinit(this); + } + } + if (d->m_pCodecParams) { avcodec_parameters_free(&d->m_pCodecParams); d->m_pCodecParams = nullptr; @@ -228,6 +235,7 @@ namespace AVQt { avcodec_parameters_to_context(d->m_pCodecCtx, d->m_pCodecParams); d->m_pCodecCtx->hw_device_ctx = av_buffer_ref(d->m_pDeviceCtx); + d->m_pCodecCtx->time_base = d->m_timebase; ret = avcodec_open2(d->m_pCodecCtx, d->m_pCodec, nullptr); if (ret != 0) { qFatal("%d: Could not open VAAPI decoder: %s", ret, av_make_error_string(strBuf, strBufSize, ret)); diff --git a/AVQt/filter/EncoderVAAPI.cpp b/AVQt/filter/EncoderVAAPI.cpp index 1c76568bd995933b4f4dcbc67934052ee35c141b..248f91fe24f36602d90825f29e9fe5ea3b6d2f09 100644 --- a/AVQt/filter/EncoderVAAPI.cpp +++ b/AVQt/filter/EncoderVAAPI.cpp @@ -45,6 +45,8 @@ namespace AVQt { int EncoderVAAPI::deinit() { Q_D(AVQt::EncoderVAAPI); + stop(); + { QMutexLocker lock(&d->m_cbListMutex); for (const auto &cb: d->m_cbList) { @@ -216,6 +218,10 @@ namespace AVQt { { QMutexLocker lock{&d->m_inputQueueMutex}; d->m_inputQueue.enqueue(queueFrame); +// std::sort(d->m_inputQueue.begin(), d->m_inputQueue.end(), +// [&](const QPair<AVFrame *, int64_t> &f1, const QPair<AVFrame *, int64_t> &f2) { +// return f1.first->pts < f2.first->pts; +// }); } } @@ -227,7 +233,7 @@ namespace AVQt { char strBuf[strBufSize]; while (d->m_running.load()) { - if (!d->m_paused.load() && !d->m_inputQueue.isEmpty()) { + if (!d->m_paused.load() && d->m_inputQueue.size() > 5) { bool shouldBe = true; if (d->m_firstFrame.compare_exchange_strong(shouldBe, false)) { auto frame = d->m_inputQueue.first().first; @@ -290,10 +296,19 @@ namespace AVQt { d->m_pCodecCtx->hw_device_ctx = av_buffer_ref(d->m_pDeviceCtx); d->m_pCodecCtx->hw_frames_ctx = av_buffer_ref(d->m_pFramesCtx); + d->m_pCodecCtx->bit_rate = 50000000; + d->m_pCodecCtx->rc_min_rate = 30000000; + d->m_pCodecCtx->rc_max_rate = 50000000; + d->m_pCodecCtx->rc_buffer_size = 100000000; + d->m_pCodecCtx->gop_size = 20; + d->m_pCodecCtx->color_primaries = AVCOL_PRI_BT2020; + d->m_pCodecCtx->color_trc = AVCOL_TRC_SMPTE2084; + d->m_pCodecCtx->colorspace = AVCOL_SPC_BT2020_NCL; + // } // d->m_pCodecCtx->pix_fmt = AV_PIX_FMT_VAAPI; - d->m_pCodecCtx->time_base = av_make_q(1, 1000000); + d->m_pCodecCtx->time_base = av_make_q(1, 1000000); // Timestamps from frame sources are always microseconds 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)); @@ -325,24 +340,44 @@ 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); + d->m_pHWFrame->pts = av_rescale_q(frame.first->pts, av_make_q(1, 1000000), + d->m_pCodecCtx->time_base); // Incoming timestamps are always microseconds +// d->m_pHWFrame->pts = frame.first->pts; av_frame_free(&frame.first); ret = avcodec_send_frame(d->m_pCodecCtx, d->m_pHWFrame); - if (ret != 0) { + qDebug("[AVQt::EncoderVAAPI] Sent frame with PTS %lld to encoder", static_cast<long long>(d->m_pHWFrame->pts)); + /*if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } else */if (ret != 0) { qFatal("%i: Could not send frame to VAAPI encoder: %s", ret, av_make_error_string(strBuf, strBufSize, ret)); } + +// avcodec_send_frame(d->m_pCodecCtx, nullptr); +// +// if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { +// break; +// } else if (ret != 0) { +// qFatal("%i: Could not flush VAAPI encoder: %s", ret, av_make_error_string(strBuf, strBufSize, ret)); +// } } 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) { qFatal("%i: Could not receive packet from encoder: %s", ret, av_make_error_string(strBuf, strBufSize, ret)); } +// packet->pts = d->m_pHWFrame->pts; +// packet->pts = d->m_pCodecCtx->coded_frame->pts; +// packet->pts = packet->dts; + packet->pos = -1; + av_packet_rescale_ts(packet, d->m_pCodecCtx->time_base, av_make_q(1, 1000000)); + qDebug("[AVQt::EncoderVAAPI] Got packet from encoder with PTS: %lld, DTS: %lld, duration: %lld, timebase: %d/%d", + static_cast<long long>(packet->pts), + static_cast<long long>(packet->dts), + static_cast<long long>(packet->duration), + d->m_pCodecCtx->time_base.num, d->m_pCodecCtx->time_base.den); if (packet->buf) { QMutexLocker lock(&d->m_cbListMutex); for (const auto &cb: d->m_cbList) { diff --git a/AVQt/output/Muxer.cpp b/AVQt/output/Muxer.cpp index 935ff345d5ff7b3d7e615227e9d1b953608c5550..2e83823f7b07887bd37c73c6e66ff788f8c3be6e 100644 --- a/AVQt/output/Muxer.cpp +++ b/AVQt/output/Muxer.cpp @@ -2,6 +2,7 @@ // Created by silas on 5/24/21. // +#include <experimental/algorithm> #include <input/IPacketSource.h> #include "private/Muxer_p.h" #include "Muxer.h" @@ -61,7 +62,7 @@ namespace AVQt { 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_pFormatContext->oformat = av_guess_format("mp4", "", 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); @@ -92,18 +93,20 @@ namespace AVQt { subtitleStream->time_base = timebase; d->m_sourceStreamMap[source].insert(subtitleStream, timebase); } - int ret = avformat_init_output(d->m_pFormatContext, nullptr); - if (ret <= 0) { + int ret = avformat_write_header(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)); } + qDebug("[AVQt::Muxer] Initialized"); } void Muxer::deinit(IPacketSource *source) { Q_D(AVQt::Muxer); - stop(nullptr); + stop(source); + qDebug("[AVQt::Muxer] deinit() called"); if (d->m_sourceStreamMap.contains(source)) { d->m_sourceStreamMap.remove(source); @@ -115,18 +118,19 @@ namespace AVQt { 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); + int ret = av_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)); } +// if (d->m_headerWritten.load()) { + av_write_trailer(d->m_pFormatContext); + qDebug("[AVQt::Muxer] Wrote trailer"); +// } avio_flush(d->m_pFormatContext->pb); - avio_closep(&d->m_pIOContext); avformat_free_context(d->m_pFormatContext); + d->m_outputDevice->close(); } } @@ -179,45 +183,52 @@ namespace AVQt { 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 (packet->pts != AV_NOPTS_VALUE) { + + 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; - } + 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, av_make_q(1, 1000000), addStream->time_base); - 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); + while (d->m_inputQueue.size() >= 100) { + QThread::msleep(2); + } - QMutexLocker lock(&d->m_inputQueueMutex); - d->m_inputQueue.enqueue(queuePacket); + QMutexLocker lock(&d->m_inputQueueMutex); + d->m_inputQueue.enqueue(queuePacket); +// std::sort(d->m_inputQueue.begin(), d->m_inputQueue.end(), &MuxerPrivate::packetQueueCompare); + } } void Muxer::run() { Q_D(AVQt::Muxer); while (d->m_running.load()) { - if (!d->m_paused.load() && !d->m_inputQueue.isEmpty()) { + if (!d->m_paused.load() && d->m_inputQueue.size() > 5) { 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) { @@ -264,4 +275,8 @@ namespace AVQt { return result ? outputDevice->pos() : AVERROR_UNKNOWN; } + + bool MuxerPrivate::packetQueueCompare(const QPair<AVPacket *, AVStream *> &packet1, const QPair<AVPacket *, AVStream *> &packet2) { + return packet1.first->dts < packet2.first->dts; + } } \ No newline at end of file diff --git a/AVQt/output/Muxer.h b/AVQt/output/Muxer.h index a0ddeeda02cdf1b994ed35483680c2924d0fb902..09d76da55fde2dd1b5408aeea2b2dc15d3349f67 100644 --- a/AVQt/output/Muxer.h +++ b/AVQt/output/Muxer.h @@ -61,4 +61,4 @@ namespace AVQt { }; } -#endif //LIBAVQT_MUXER_H +#endif //LIBAVQT_MUXER_H \ No newline at end of file diff --git a/AVQt/output/private/Muxer_p.h b/AVQt/output/private/Muxer_p.h index cbe1523645755427e74bf816403d36950e46039d..ca43c3509aa082d74f34d217019e48d850edd489 100644 --- a/AVQt/output/private/Muxer_p.h +++ b/AVQt/output/private/Muxer_p.h @@ -47,7 +47,9 @@ namespace AVQt { static int64_t seekIO(void *opaque, int64_t offset, int whence); static int writeToIO(void *opaque, uint8_t *buf, int buf_size); + + static bool packetQueueCompare(const QPair<AVPacket *, AVStream *> &packet1, const QPair<AVPacket *, AVStream *> &packet2); }; } -#endif //LIBAVQT_MUXER_P_H +#endif //LIBAVQT_MUXER_P_H \ No newline at end of file diff --git a/Player/main.cpp b/Player/main.cpp index 7a4cef9d521e602b0ce52c88599296c90e85133a..a4623d4f5d22aafbcbdd9d0cc7a7ee9e43f33367 100644 --- a/Player/main.cpp +++ b/Player/main.cpp @@ -76,16 +76,17 @@ int main(int argc, char *argv[]) { #endif AVQt::OpenGLRenderer renderer; -// AVQt::IEncoder *encoder = new AVQt::EncoderVAAPI("hevc_vaapi"); + AVQt::IEncoder *encoder = new AVQt::EncoderVAAPI("hevc_vaapi"); demuxer.registerCallback(videoDecoder, AVQt::IPacketSource::CB_VIDEO); -// videoDecoder->registerCallback(encoder); + videoDecoder->registerCallback(encoder); -// QFile outputFile("output.ts"); -// outputFile.open(QIODevice::ReadWrite | QIODevice::Truncate); -// AVQt::Muxer muxer(&outputFile); + QFile outputFile("output.mp4"); + outputFile.open(QIODevice::ReadWrite | QIODevice::Truncate); + outputFile.seek(0); + AVQt::Muxer muxer(&outputFile); -// encoder->registerCallback(&muxer, AVQt::IPacketSource::CB_VIDEO); + encoder->registerCallback(&muxer, AVQt::IPacketSource::CB_VIDEO); videoDecoder->registerCallback(&renderer); renderer.setMinimumSize(QSize(360, 240)); @@ -105,7 +106,7 @@ int main(int argc, char *argv[]) { QObject::connect(app, &QApplication::aboutToQuit, [&] { demuxer.deinit(); -// delete encoder; + delete encoder; delete videoDecoder; });