Skip to content

Commit

Permalink
入力ファイルの字幕のタイムスタンプが入れ違いになっている場合にこれを入力側でソートして解決するように変更。
Browse files Browse the repository at this point in the history
  • Loading branch information
rigaya committed Nov 12, 2024
1 parent fb80e62 commit 4a673cf
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 5 deletions.
6 changes: 6 additions & 0 deletions QSVEnc/QSVEnc_readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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での動作に対応。
Expand Down
4 changes: 3 additions & 1 deletion QSVPipeline/rgy_input.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<AVPacket*> subPacketTemporalBuffer; //字幕のタイムスタンプが入れ違いになっているのを解決する一時的なキュー

AVDemuxStream() :
index(0),
Expand All @@ -100,7 +101,8 @@ struct AVDemuxStream {
timebase({ 0, 0 }),
subtitleHeader(nullptr),
subtitleHeaderSize(0),
lang() {
lang(),
subPacketTemporalBuffer() {
};
};

Expand Down
72 changes: 68 additions & 4 deletions QSVPipeline/rgy_input_avcodec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ AVDemuxFormat::AVDemuxFormat() :
fpInput(nullptr),
inputBuffer(nullptr),
inputBufferSize(0),
inputFilesize(0) {
inputFilesize(0),
subPacketTemporalBufferIntervalCount(-1) {
}

void AVDemuxFormat::close(RGYLog *log) {
Expand Down Expand Up @@ -2657,6 +2658,47 @@ AVDemuxStream *RGYInputAvcodec::getPacketStreamData(const AVPacket *pkt) {
return nullptr;
}

//subPacketTemporalBufferにたまっている字幕パケットをソートして送出する
void RGYInputAvcodec::sortAndPushSubtitlePacket() {
for (auto& st : m_Demux.stream) {
std::vector<int64_t> 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<int, std::unique_ptr<AVPacket, RGYAVDeleter<AVPacket>>> RGYInputAvcodec::getSample(bool bTreatFirstPacketAsKeyframe) {
int i_samples = 0;
int ret_read_frame = 0;
Expand All @@ -2678,6 +2720,19 @@ std::tuple<int, std::unique_ptr<AVPacket, RGYAVDeleter<AVPacket>>> 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;
Expand Down Expand Up @@ -2782,14 +2837,21 @@ std::tuple<int, std::unique_ptr<AVPacket, RGYAVDeleter<AVPacket>>> 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();
Expand All @@ -2799,6 +2861,8 @@ std::tuple<int, std::unique_ptr<AVPacket, RGYAVDeleter<AVPacket>>> 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();
Expand Down
5 changes: 5 additions & 0 deletions QSVPipeline/rgy_input_avcodec.h
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,8 @@ struct AVDemuxFormat {
int inputBufferSize; //入力バッファサイズ
uint64_t inputFilesize; //入力ファイルサイズ

int64_t subPacketTemporalBufferIntervalCount; //字幕のタイムスタンプが入れ違いになっているのを解決する一時的なキューに登録を行ってから他のパケットを取得した数

AVDemuxFormat();
~AVDemuxFormat() { close(); }
void close(RGYLog *log = nullptr);
Expand Down Expand Up @@ -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のヘッダの修正を行う
Expand Down

0 comments on commit 4a673cf

Please sign in to comment.