From 4a673cf7c3673dcb0ceffe857a8d85b2259ed417 Mon Sep 17 00:00:00 2001 From: rigaya Date: Tue, 12 Nov 2024 18:46:33 +0900 Subject: [PATCH] =?UTF-8?q?=E5=85=A5=E5=8A=9B=E3=83=95=E3=82=A1=E3=82=A4?= =?UTF-8?q?=E3=83=AB=E3=81=AE=E5=AD=97=E5=B9=95=E3=81=AE=E3=82=BF=E3=82=A4?= =?UTF-8?q?=E3=83=A0=E3=82=B9=E3=82=BF=E3=83=B3=E3=83=97=E3=81=8C=E5=85=A5?= =?UTF-8?q?=E3=82=8C=E9=81=95=E3=81=84=E3=81=AB=E3=81=AA=E3=81=A3=E3=81=A6?= =?UTF-8?q?=E3=81=84=E3=82=8B=E5=A0=B4=E5=90=88=E3=81=AB=E3=81=93=E3=82=8C?= =?UTF-8?q?=E3=82=92=E5=85=A5=E5=8A=9B=E5=81=B4=E3=81=A7=E3=82=BD=E3=83=BC?= =?UTF-8?q?=E3=83=88=E3=81=97=E3=81=A6=E8=A7=A3=E6=B1=BA=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E5=A4=89=E6=9B=B4=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- QSVEnc/QSVEnc_readme.txt | 6 +++ QSVPipeline/rgy_input.h | 4 +- QSVPipeline/rgy_input_avcodec.cpp | 72 +++++++++++++++++++++++++++++-- QSVPipeline/rgy_input_avcodec.h | 5 +++ 4 files changed, 82 insertions(+), 5 deletions(-) diff --git a/QSVEnc/QSVEnc_readme.txt b/QSVEnc/QSVEnc_readme.txt index 1ce73021..f2b0f2de 100644 --- a/QSVEnc/QSVEnc_readme.txt +++ b/QSVEnc/QSVEnc_readme.txt @@ -313,6 +313,12 @@ API v1.1 … Intel Media SDK v2.0 【どうでもいいメモ】 +2024.11.12 (7.73) +- --dolby-vision-rpu copyを使用して長時間のエンコードを行うと、エンコード速度が著しく低下していくのを改善し、速度を維持し続けられるように。 +- AV1出力時に--dhdr10-infoを使用した時の出力を改善。 +- 入力ファイルの字幕のタイムスタンプが入れ違いになっている場合にエラーが発生していたのを、ソートしなおして解決するように変更。 +- --vpp-tweakをなにもしない設定で実行した時クラッシュするのを回避。 + 2024.11.02 (7.72) [QSVEncC] - --dhdr10-infoの実装を変更し、Linuxでの動作に対応。 diff --git a/QSVPipeline/rgy_input.h b/QSVPipeline/rgy_input.h index e7cd9426..e73abd66 100644 --- a/QSVPipeline/rgy_input.h +++ b/QSVPipeline/rgy_input.h @@ -81,6 +81,7 @@ struct AVDemuxStream { void *subtitleHeader; //stream = nullptrの場合 caption2assのヘッダー情報 (srt形式でもass用のヘッダーが入っている) int subtitleHeaderSize; //stream = nullptrの場合 caption2assのヘッダー情報のサイズ char lang[4]; //trackの言語情報(3文字) + std::vector subPacketTemporalBuffer; //字幕のタイムスタンプが入れ違いになっているのを解決する一時的なキュー AVDemuxStream() : index(0), @@ -100,7 +101,8 @@ struct AVDemuxStream { timebase({ 0, 0 }), subtitleHeader(nullptr), subtitleHeaderSize(0), - lang() { + lang(), + subPacketTemporalBuffer() { }; }; diff --git a/QSVPipeline/rgy_input_avcodec.cpp b/QSVPipeline/rgy_input_avcodec.cpp index 43401e74..351a4299 100644 --- a/QSVPipeline/rgy_input_avcodec.cpp +++ b/QSVPipeline/rgy_input_avcodec.cpp @@ -72,7 +72,8 @@ AVDemuxFormat::AVDemuxFormat() : fpInput(nullptr), inputBuffer(nullptr), inputBufferSize(0), - inputFilesize(0) { + inputFilesize(0), + subPacketTemporalBufferIntervalCount(-1) { } void AVDemuxFormat::close(RGYLog *log) { @@ -2657,6 +2658,47 @@ AVDemuxStream *RGYInputAvcodec::getPacketStreamData(const AVPacket *pkt) { return nullptr; } +//subPacketTemporalBufferにたまっている字幕パケットをソートして送出する +void RGYInputAvcodec::sortAndPushSubtitlePacket() { + for (auto& st : m_Demux.stream) { + std::vector ptsList; // オリジナルのptsを保存しておく + ptsList.reserve(st.subPacketTemporalBuffer.size()); + for (const auto& pkt : st.subPacketTemporalBuffer) { + ptsList.push_back(pkt->pts); + } + std::sort(st.subPacketTemporalBuffer.begin(), st.subPacketTemporalBuffer.end(), [](const auto pkt1, const auto pkt2) { + return pkt1->pts < pkt2->pts; + }); + int ptsMismatchStart = -1; + int ptsMismatchFin = -1; + for (int i = 0; i < (int)ptsList.size(); ++i) { + if (ptsList[i] != st.subPacketTemporalBuffer[i]->pts) { + if (ptsMismatchStart < 0) ptsMismatchStart = i; + ptsMismatchFin = i; + } + } + if (ptsMismatchStart >= 0) { + tstring sortMes; + sortMes += strsprintf(_T("subtitle packet pts sorted for track #%d\nsubtitle input pts :"), st.index); + for (int i = ptsMismatchStart; i <= ptsMismatchFin; ++i) { + sortMes += strsprintf(_T("%lld "), (long long int)ptsList[i]); + } + sortMes += strsprintf(_T("\nsubtitle sorted pts :")); + for (int i = ptsMismatchStart; i <= ptsMismatchFin; ++i) { + sortMes += strsprintf(_T("%lld "), (long long int)st.subPacketTemporalBuffer[i]->pts); + } + sortMes += _T("\n"); + AddMessage(RGY_LOG_WARN, sortMes); + } + + for (auto& pkt : st.subPacketTemporalBuffer) { + m_Demux.qStreamPktL1.push_back(pkt); + } + st.subPacketTemporalBuffer.clear(); + } + m_Demux.format.subPacketTemporalBufferIntervalCount = -1; +} + std::tuple>> RGYInputAvcodec::getSample(bool bTreatFirstPacketAsKeyframe) { int i_samples = 0; int ret_read_frame = 0; @@ -2678,6 +2720,19 @@ std::tuple>> RGYInputAvcod pkt->dts == AV_NOPTS_VALUE ? " Unknown" : strsprintf("%12lld", (long long int)pkt->dts).c_str(), (long long int)pkt->duration, pkt->flags, (long long int)pkt->pos); } + if (m_Demux.format.subPacketTemporalBufferIntervalCount >= 0) { // 字幕パケットがバッファにある + m_Demux.format.subPacketTemporalBufferIntervalCount += m_Demux.format.formatCtx->streams[pkt->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ? 1 : 0; + // 字幕パケットの場合、パケットのタイムスタンプの順序が入れ替わっている場合がある + // これを修正するため、いったんバッファにためておき、 + // 一定期間すぎたり(m_Demux.format.subPacketTemporalBufferIntervalCount >= subSortPacketIntervalByVideoFrames) + // バッファに字幕パケットがたくさんたまってきたらソートして出力するようにする + static const int subSortPacketIntervalByVideoFrames = 10; + static const size_t subSortPacketTemporalBufferThreshold = 50; + if (m_Demux.format.subPacketTemporalBufferIntervalCount >= subSortPacketIntervalByVideoFrames + || std::accumulate(m_Demux.stream.begin(), m_Demux.stream.end(), (size_t)0, [](size_t sum, const AVDemuxStream& st) { return sum + st.subPacketTemporalBuffer.size(); }) >= subSortPacketTemporalBufferThreshold) { + sortAndPushSubtitlePacket(); + } + } if (pkt->stream_index == m_Demux.video.index) { if (pkt->flags & AV_PKT_FLAG_CORRUPT) { const auto timestamp = (pkt->pts == AV_NOPTS_VALUE) ? pkt->dts : pkt->pts; @@ -2782,14 +2837,21 @@ std::tuple>> RGYInputAvcod CheckAndMoveStreamPacketList(); return { 0, std::move(pkt) }; } - const auto *stream = getPacketStreamData(pkt.get()); + auto *stream = getPacketStreamData(pkt.get()); if (stream != nullptr) { if (pkt->flags & AV_PKT_FLAG_CORRUPT) { const auto timestamp = (pkt->pts == AV_NOPTS_VALUE) ? pkt->dts : pkt->pts; AddMessage(RGY_LOG_WARN, _T("corrupt packet in stream %d: %lld (%s)\n"), pkt->stream_index, (long long int)timestamp, getTimestampString(timestamp, stream->stream->time_base).c_str()); } - //音声/字幕パケットはひとまずすべてバッファに格納する - m_Demux.qStreamPktL1.push_back(pkt.release()); + if (stream->stream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) { + // 字幕パケットの場合、パケットのタイムスタンプの順序が入れ替わっている場合がある + // これを修正するため、いったんバッファにためておき、一定期間すぎたらソートして出力するようにする + stream->subPacketTemporalBuffer.push_back(pkt.release()); + m_Demux.format.subPacketTemporalBufferIntervalCount = 0; // カウンタをリセット + } else { + //音声/字幕パケットはひとまずすべてバッファに格納する + m_Demux.qStreamPktL1.push_back(pkt.release()); + } } } pkt.reset(); @@ -2799,6 +2861,8 @@ std::tuple>> RGYInputAvcod return { 1, nullptr }; } AddMessage(RGY_LOG_DEBUG, _T("%d frames, %s\n"), m_Demux.frames.frameNum(), qsv_av_err2str(ret_read_frame).c_str()); + //たまっている字幕があれば送出する + sortAndPushSubtitlePacket(); //動画の終端を表す最後のptsを挿入する int64_t videoFinPts = 0; const int nFrameNum = m_Demux.frames.frameNum(); diff --git a/QSVPipeline/rgy_input_avcodec.h b/QSVPipeline/rgy_input_avcodec.h index 0fb21919..dd219f32 100644 --- a/QSVPipeline/rgy_input_avcodec.h +++ b/QSVPipeline/rgy_input_avcodec.h @@ -718,6 +718,8 @@ struct AVDemuxFormat { int inputBufferSize; //入力バッファサイズ uint64_t inputFilesize; //入力ファイルサイズ + int64_t subPacketTemporalBufferIntervalCount; //字幕のタイムスタンプが入れ違いになっているのを解決する一時的なキューに登録を行ってから他のパケットを取得した数 + AVDemuxFormat(); ~AVDemuxFormat() { close(); } void close(RGYLog *log = nullptr); @@ -1012,6 +1014,9 @@ class RGYInputAvcodec : public RGYInput //ptsを動画のtimebaseから音声のtimebaseに変換する int64_t convertTimebaseVidToStream(int64_t pts, const AVDemuxStream *stream); + //subPacketTemporalBufferにたまっている字幕パケットをソートして送出する + void sortAndPushSubtitlePacket(); + void hevcMp42Annexb(AVPacket *pkt); //VC-1のヘッダの修正を行う