Format the isolated encoder capability and live setup passes
This commit is contained in:
1 parent
7ad0aa61af
commit
8fda2b0751
1 file changed
+80
-80
@@ -82,91 +82,91 @@ bool StreamVideoEncoder::open(const StreamVideoConfig& config) {
|
||||
if (!encoder) continue;
|
||||
// Проба не оставляет references/PTS в live-контексте: после двух KEY encoder открывается заново.
|
||||
for (int pass = 0; pass < 2; pass++) {
|
||||
close(); m->config = config; m->name = QString::fromLatin1(name);
|
||||
m->codec = avcodec_alloc_context3(encoder);
|
||||
if (!m->codec) { m->error = "Video encoder allocation failed"; break; }
|
||||
auto* c = m->codec;
|
||||
c->width = config.width; c->height = config.height; c->time_base = {1, 1000000}; c->framerate = {config.fps, 1};
|
||||
c->bit_rate = config.bitrate; c->rc_max_rate = config.bitrate; c->rc_buffer_size = config.bitrate / 4;
|
||||
c->gop_size = config.fps; c->max_b_frames = 0; c->thread_count = 2;
|
||||
c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER | AV_CODEC_FLAG_CLOSED_GOP;
|
||||
c->color_range = AVCOL_RANGE_MPEG; c->colorspace = AVCOL_SPC_BT709;
|
||||
c->color_primaries = AVCOL_PRI_BT709; c->color_trc = AVCOL_TRC_BT709;
|
||||
AVPixelFormat sw = softwareFormat(encoder);
|
||||
int result = 0;
|
||||
if (name.endsWith("_vaapi")) {
|
||||
sw = AV_PIX_FMT_NV12; c->pix_fmt = AV_PIX_FMT_VAAPI;
|
||||
result = av_hwdevice_ctx_create(&m->device, AV_HWDEVICE_TYPE_VAAPI, nullptr, nullptr, 0);
|
||||
close(); m->config = config; m->name = QString::fromLatin1(name);
|
||||
m->codec = avcodec_alloc_context3(encoder);
|
||||
if (!m->codec) { m->error = "Video encoder allocation failed"; break; }
|
||||
auto* c = m->codec;
|
||||
c->width = config.width; c->height = config.height; c->time_base = {1, 1000000}; c->framerate = {config.fps, 1};
|
||||
c->bit_rate = config.bitrate; c->rc_max_rate = config.bitrate; c->rc_buffer_size = config.bitrate / 4;
|
||||
c->gop_size = config.fps; c->max_b_frames = 0; c->thread_count = 2;
|
||||
c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER | AV_CODEC_FLAG_CLOSED_GOP;
|
||||
c->color_range = AVCOL_RANGE_MPEG; c->colorspace = AVCOL_SPC_BT709;
|
||||
c->color_primaries = AVCOL_PRI_BT709; c->color_trc = AVCOL_TRC_BT709;
|
||||
AVPixelFormat sw = softwareFormat(encoder);
|
||||
int result = 0;
|
||||
if (name.endsWith("_vaapi")) {
|
||||
sw = AV_PIX_FMT_NV12; c->pix_fmt = AV_PIX_FMT_VAAPI;
|
||||
result = av_hwdevice_ctx_create(&m->device, AV_HWDEVICE_TYPE_VAAPI, nullptr, nullptr, 0);
|
||||
if (result >= 0) {
|
||||
c->hw_frames_ctx = av_hwframe_ctx_alloc(m->device);
|
||||
if (!c->hw_frames_ctx) result = AVERROR(ENOMEM);
|
||||
else {
|
||||
auto* frames = reinterpret_cast<AVHWFramesContext*>(c->hw_frames_ctx->data);
|
||||
frames->format = c->pix_fmt; frames->sw_format = sw; frames->width = c->width; frames->height = c->height;
|
||||
frames->initial_pool_size = 4; result = av_hwframe_ctx_init(c->hw_frames_ctx);
|
||||
}
|
||||
}
|
||||
} else c->pix_fmt = sw;
|
||||
if (sw == AV_PIX_FMT_NONE) result = AVERROR(ENOSYS);
|
||||
AVDictionary* options = nullptr;
|
||||
if (name == "libx264" || name == "libx265") {
|
||||
av_dict_set(&options, "preset", "ultrafast", 0); av_dict_set(&options, "tune", "zerolatency", 0);
|
||||
av_dict_set(&options, "forced-idr", "1", 0);
|
||||
if (name == "libx264") av_dict_set(&options, "x264-params", "scenecut=0:open-gop=0:repeat-headers=0", 0);
|
||||
else av_dict_set(&options, "x265-params", "bframes=0:rc-lookahead=0:open-gop=0:pools=1:frame-threads=1:log-level=error", 0);
|
||||
} else if (name.endsWith("_nvenc")) {
|
||||
av_dict_set(&options, "preset", "p1", 0); av_dict_set(&options, "tune", "ull", 0);
|
||||
av_dict_set(&options, "zerolatency", "1", 0); av_dict_set(&options, "rc-lookahead", "0", 0);
|
||||
av_dict_set(&options, "forced-idr", "1", 0);
|
||||
} else if (name == "libvpx-vp9" || name == "libaom-av1") {
|
||||
av_dict_set(&options, "deadline", "realtime", 0); av_dict_set(&options, "cpu-used", "8", 0);
|
||||
av_dict_set(&options, "lag-in-frames", "0", 0); av_dict_set(&options, "row-mt", "1", 0);
|
||||
} else if (name.endsWith("_qsv")) {
|
||||
av_dict_set(&options, "async_depth", "1", 0); av_dict_set(&options, "look_ahead", "0", 0);
|
||||
} else if (name.endsWith("_vaapi")) {
|
||||
av_dict_set(&options, "async_depth", "1", 0);
|
||||
} else if (name == "libvvenc") av_dict_set(&options, "preset", "fast", 0);
|
||||
if (result >= 0) result = avcodec_open2(c, encoder, &options);
|
||||
av_dict_free(&options);
|
||||
if (result >= 0) {
|
||||
c->hw_frames_ctx = av_hwframe_ctx_alloc(m->device);
|
||||
if (!c->hw_frames_ctx) result = AVERROR(ENOMEM);
|
||||
m->frame = av_frame_alloc();
|
||||
if (!m->frame) result = AVERROR(ENOMEM);
|
||||
else {
|
||||
auto* frames = reinterpret_cast<AVHWFramesContext*>(c->hw_frames_ctx->data);
|
||||
frames->format = c->pix_fmt; frames->sw_format = sw; frames->width = c->width; frames->height = c->height;
|
||||
frames->initial_pool_size = 4; result = av_hwframe_ctx_init(c->hw_frames_ctx);
|
||||
m->frame->format = sw; m->frame->width = c->width; m->frame->height = c->height;
|
||||
m->frame->color_range = c->color_range; m->frame->colorspace = c->colorspace;
|
||||
result = av_frame_get_buffer(m->frame, 32);
|
||||
}
|
||||
}
|
||||
} else c->pix_fmt = sw;
|
||||
if (sw == AV_PIX_FMT_NONE) result = AVERROR(ENOSYS);
|
||||
AVDictionary* options = nullptr;
|
||||
if (name == "libx264" || name == "libx265") {
|
||||
av_dict_set(&options, "preset", "ultrafast", 0); av_dict_set(&options, "tune", "zerolatency", 0);
|
||||
av_dict_set(&options, "forced-idr", "1", 0);
|
||||
if (name == "libx264") av_dict_set(&options, "x264-params", "scenecut=0:open-gop=0:repeat-headers=0", 0);
|
||||
else av_dict_set(&options, "x265-params", "bframes=0:rc-lookahead=0:open-gop=0:pools=1:frame-threads=1:log-level=error", 0);
|
||||
} else if (name.endsWith("_nvenc")) {
|
||||
av_dict_set(&options, "preset", "p1", 0); av_dict_set(&options, "tune", "ull", 0);
|
||||
av_dict_set(&options, "zerolatency", "1", 0); av_dict_set(&options, "rc-lookahead", "0", 0);
|
||||
av_dict_set(&options, "forced-idr", "1", 0);
|
||||
} else if (name == "libvpx-vp9" || name == "libaom-av1") {
|
||||
av_dict_set(&options, "deadline", "realtime", 0); av_dict_set(&options, "cpu-used", "8", 0);
|
||||
av_dict_set(&options, "lag-in-frames", "0", 0); av_dict_set(&options, "row-mt", "1", 0);
|
||||
} else if (name.endsWith("_qsv")) {
|
||||
av_dict_set(&options, "async_depth", "1", 0); av_dict_set(&options, "look_ahead", "0", 0);
|
||||
} else if (name.endsWith("_vaapi")) {
|
||||
av_dict_set(&options, "async_depth", "1", 0);
|
||||
} else if (name == "libvvenc") av_dict_set(&options, "preset", "fast", 0);
|
||||
if (result >= 0) result = avcodec_open2(c, encoder, &options);
|
||||
av_dict_free(&options);
|
||||
if (result >= 0) {
|
||||
m->frame = av_frame_alloc();
|
||||
if (!m->frame) result = AVERROR(ENOMEM);
|
||||
else {
|
||||
m->frame->format = sw; m->frame->width = c->width; m->frame->height = c->height;
|
||||
m->frame->color_range = c->color_range; m->frame->colorspace = c->colorspace;
|
||||
result = av_frame_get_buffer(m->frame, 32);
|
||||
const char* filter = config.codec == STREAM_MEDIA_H264 ? "h264_mp4toannexb"
|
||||
: config.codec == STREAM_MEDIA_H265 ? "hevc_mp4toannexb" : config.codec == STREAM_MEDIA_H266 ? "vvc_mp4toannexb" : nullptr;
|
||||
if (result >= 0 && filter) {
|
||||
auto* bsf = av_bsf_get_by_name(filter);
|
||||
result = bsf ? av_bsf_alloc(bsf, &m->filter) : AVERROR(ENOSYS);
|
||||
if (result >= 0) result = avcodec_parameters_from_context(m->filter->par_in, c);
|
||||
if (result >= 0) { m->filter->time_base_in = c->time_base; result = av_bsf_init(m->filter); }
|
||||
}
|
||||
}
|
||||
const char* filter = config.codec == STREAM_MEDIA_H264 ? "h264_mp4toannexb"
|
||||
: config.codec == STREAM_MEDIA_H265 ? "hevc_mp4toannexb" : config.codec == STREAM_MEDIA_H266 ? "vvc_mp4toannexb" : nullptr;
|
||||
if (result >= 0 && filter) {
|
||||
auto* bsf = av_bsf_get_by_name(filter);
|
||||
result = bsf ? av_bsf_alloc(bsf, &m->filter) : AVERROR(ENOSYS);
|
||||
if (result >= 0) result = avcodec_parameters_from_context(m->filter->par_in, c);
|
||||
if (result >= 0) { m->filter->time_base_in = c->time_base; result = av_bsf_init(m->filter); }
|
||||
}
|
||||
if (result < 0) {
|
||||
m->error = ffError(result);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_VIDEO, "desktop encoder unavailable name=%s size=%dx%d error=%s",
|
||||
name.constData(), config.width, config.height, qPrintable(m->error)); break;
|
||||
}
|
||||
if (!pass) {
|
||||
QImage probe(config.width, config.height, QImage::Format_RGB32); probe.fill(Qt::black);
|
||||
QList<StreamEncodedFrame> output;
|
||||
if (!encode(probe, 0, true, output) || output.isEmpty() || !output.front().key) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_VIDEO, "desktop encoder probe lacks immediate KEY name=%s error=%s", name.constData(), qPrintable(m->error));
|
||||
break;
|
||||
}
|
||||
output.clear();
|
||||
if (!encode(probe, 1000000 / config.fps, true, output) || output.isEmpty() || !output.front().key) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_VIDEO, "desktop encoder cannot force an independent KEY name=%s error=%s", name.constData(), qPrintable(m->error));
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop encoder ready name=%s codec=%d size=%dx%d fps=%d bitrate=%d two_KEYs=verified",
|
||||
name.constData(), config.codec, config.width, config.height, config.fps, config.bitrate);
|
||||
return true;
|
||||
if (result < 0) {
|
||||
m->error = ffError(result);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_VIDEO, "desktop encoder unavailable name=%s size=%dx%d error=%s",
|
||||
name.constData(), config.width, config.height, qPrintable(m->error)); break;
|
||||
}
|
||||
if (!pass) {
|
||||
QImage probe(config.width, config.height, QImage::Format_RGB32); probe.fill(Qt::black);
|
||||
QList<StreamEncodedFrame> output;
|
||||
if (!encode(probe, 0, true, output) || output.isEmpty() || !output.front().key) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_VIDEO, "desktop encoder probe lacks immediate KEY name=%s error=%s", name.constData(), qPrintable(m->error));
|
||||
break;
|
||||
}
|
||||
output.clear();
|
||||
if (!encode(probe, 1000000 / config.fps, true, output) || output.isEmpty() || !output.front().key) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_VIDEO, "desktop encoder cannot force an independent KEY name=%s error=%s", name.constData(), qPrintable(m->error));
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop encoder ready name=%s codec=%d size=%dx%d fps=%d bitrate=%d two_KEYs=verified",
|
||||
name.constData(), config.codec, config.width, config.height, config.fps, config.bitrate);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (m->error.isEmpty()) m->error = "No working encoder for " + codecName(config.codec);
|
||||
|
||||
Reference in new issue
Block a user