Extract live video encoder naming for reuse by calls and broadcasts
This commit is contained in:
1 parent
f89494fb4f
commit
dca1adc173
2 files changed
+28
-24
No files matched your search
+16
-16
@@ -1,4 +1,4 @@
|
||||
#include "stream_video_encoder.h"
|
||||
#include "live_video_encoder.h"
|
||||
#include "debug_config.h"
|
||||
#include <QPainter>
|
||||
#include <algorithm>
|
||||
@@ -14,9 +14,9 @@ extern "C" {
|
||||
#endif
|
||||
}
|
||||
|
||||
struct StreamVideoEncoder::Impl {
|
||||
struct LiveVideoEncoder::Impl {
|
||||
QString error, name;
|
||||
StreamVideoConfig config;
|
||||
LiveVideoConfig config;
|
||||
#ifdef HAVE_FFMPEG
|
||||
AVCodecContext* codec = nullptr;
|
||||
AVBSFContext* filter = nullptr;
|
||||
@@ -29,12 +29,12 @@ struct StreamVideoEncoder::Impl {
|
||||
}
|
||||
#endif
|
||||
};
|
||||
StreamVideoEncoder::StreamVideoEncoder() : m(std::make_unique<Impl>()) {}
|
||||
StreamVideoEncoder::~StreamVideoEncoder() = default;
|
||||
void StreamVideoEncoder::close() { m = std::make_unique<Impl>(); }
|
||||
QString StreamVideoEncoder::error() const { return m->error; }
|
||||
QString StreamVideoEncoder::name() const { return m->name; }
|
||||
QString StreamVideoEncoder::codecName(int codec) {
|
||||
LiveVideoEncoder::LiveVideoEncoder() : m(std::make_unique<Impl>()) {}
|
||||
LiveVideoEncoder::~LiveVideoEncoder() = default;
|
||||
void LiveVideoEncoder::close() { m = std::make_unique<Impl>(); }
|
||||
QString LiveVideoEncoder::error() const { return m->error; }
|
||||
QString LiveVideoEncoder::name() const { return m->name; }
|
||||
QString LiveVideoEncoder::codecName(int codec) {
|
||||
static const char* names[] = {"", "H.264", "H.265", "VP9", "AV1", "H.266"};
|
||||
return codec >= 1 && codec <= 5 ? QString::fromLatin1(names[codec]) : QString();
|
||||
}
|
||||
@@ -69,7 +69,7 @@ AVPixelFormat softwareFormat(const AVCodec* codec) {
|
||||
}
|
||||
#endif
|
||||
|
||||
bool StreamVideoEncoder::open(const StreamVideoConfig& config) {
|
||||
bool LiveVideoEncoder::open(const LiveVideoConfig& config) {
|
||||
close();
|
||||
if (config.width < 16 || config.height < 16 || config.width > 4096 || config.height > 4096
|
||||
|| config.width % 2 || config.height % 2 || config.fps < 1 || config.fps > 60
|
||||
@@ -152,7 +152,7 @@ bool StreamVideoEncoder::open(const StreamVideoConfig& config) {
|
||||
}
|
||||
if (!pass) {
|
||||
QImage probe(config.width, config.height, QImage::Format_RGB32); probe.fill(Qt::black);
|
||||
QList<StreamEncodedFrame> output;
|
||||
QList<LiveEncodedFrame> 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;
|
||||
@@ -176,7 +176,7 @@ bool StreamVideoEncoder::open(const StreamVideoConfig& config) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop encoder open failed: %s", qPrintable(m->error)); return false;
|
||||
}
|
||||
|
||||
bool StreamVideoEncoder::encode(const QImage& image, uint64_t pts, bool key, QList<StreamEncodedFrame>& output) {
|
||||
bool LiveVideoEncoder::encode(const QImage& image, uint64_t pts, bool key, QList<LiveEncodedFrame>& output) {
|
||||
#ifdef HAVE_FFMPEG
|
||||
if (!m->codec || !m->frame || image.isNull() || pts > INT64_MAX) {
|
||||
m->error = "Invalid video frame"; DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop encode invalid frame/state"); return false;
|
||||
@@ -217,7 +217,7 @@ bool StreamVideoEncoder::encode(const QImage& image, uint64_t pts, bool key, QLi
|
||||
if (result >= 0) result = av_bsf_receive_packet(m->filter, packet);
|
||||
if (result < 0) break;
|
||||
}
|
||||
StreamEncodedFrame encoded; encoded.key = (packet->flags & AV_PKT_FLAG_KEY) != 0;
|
||||
LiveEncodedFrame encoded; encoded.key = (packet->flags & AV_PKT_FLAG_KEY) != 0;
|
||||
encoded.pts = packet->pts >= 0 ? static_cast<uint64_t>(packet->pts) : pts;
|
||||
if (key) DEBUG_DEBUG(DEBUG_CATEGORY_VIDEO, "desktop requested KEY name=%s input_PTS=%llu output_PTS=%llu key=%d flags=%x bytes=%d",
|
||||
qPrintable(m->name), (unsigned long long)pts, (unsigned long long)encoded.pts, encoded.key, packet->flags, packet->size);
|
||||
@@ -246,7 +246,7 @@ bool StreamVideoEncoder::encode(const QImage& image, uint64_t pts, bool key, QLi
|
||||
#endif
|
||||
}
|
||||
|
||||
QList<int> StreamVideoEncoder::available() {
|
||||
QList<int> LiveVideoEncoder::available() {
|
||||
QList<int> result;
|
||||
for (int codec = 1; codec <= 5; codec++) {
|
||||
#ifdef HAVE_FFMPEG
|
||||
@@ -254,13 +254,13 @@ QList<int> StreamVideoEncoder::available() {
|
||||
for (const auto& name : encoders(codec)) if (avcodec_find_encoder_by_name(name.constData())) { installed = true; break; }
|
||||
if (!installed) { DEBUG_DEBUG(DEBUG_CATEGORY_VIDEO, "desktop codec has no installed encoder codec=%d", codec); continue; }
|
||||
#endif
|
||||
StreamVideoEncoder encoder; StreamVideoConfig config; config.width = 320; config.height = 180; config.codec = codec;
|
||||
LiveVideoEncoder encoder; LiveVideoConfig config; config.width = 320; config.height = 180; config.codec = codec;
|
||||
if (encoder.open(config)) result.push_back(codec);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool StreamVideoEncoder::setBitrate(int bitrate) {
|
||||
bool LiveVideoEncoder::setBitrate(int bitrate) {
|
||||
if (bitrate < 64000 || bitrate > 16000000) { DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop live bitrate invalid value=%d", bitrate); return false; }
|
||||
#ifdef HAVE_FFMPEG
|
||||
// libx264 перечитывает эти три значения через reconfig_encoder при подаче следующего кадра.
|
||||
@@ -9,19 +9,23 @@
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
||||
struct StreamVideoConfig {
|
||||
enum class LiveVideoProfile { Automatic, H264ConstrainedBaseline, H265Main };
|
||||
struct LiveVideoConfig {
|
||||
int width = 1280, height = 720, fps = 30, bitrate = 2500000, codec = 1, rotation = 0;
|
||||
bool operator==(const StreamVideoConfig&) const = default;
|
||||
LiveVideoProfile profile = LiveVideoProfile::Automatic;
|
||||
int level = 0, references = 0, vbvBits = 0; // level: AVC level_idc / HEVC general_level_idc; 0 — auto.
|
||||
QString backend; // Пусто — проба доступных; непусто — только уже проверенный encoder.
|
||||
bool operator==(const LiveVideoConfig&) const = default;
|
||||
};
|
||||
struct StreamEncodedFrame { QByteArray data; uint64_t pts = 0; bool key = false; };
|
||||
class StreamVideoEncoder {
|
||||
struct LiveEncodedFrame { QByteArray data; uint64_t pts = 0; bool key = false; };
|
||||
class LiveVideoEncoder {
|
||||
public:
|
||||
StreamVideoEncoder();
|
||||
~StreamVideoEncoder();
|
||||
bool open(const StreamVideoConfig& config);
|
||||
LiveVideoEncoder();
|
||||
~LiveVideoEncoder();
|
||||
bool open(const LiveVideoConfig& config);
|
||||
bool setBitrate(int bitrate); // true — изменение на следующем кадре; false — нужна смена видеоконтекста
|
||||
void close();
|
||||
bool encode(const QImage& image, uint64_t pts, bool key, QList<StreamEncodedFrame>& output);
|
||||
bool encode(const QImage& image, uint64_t pts, bool key, QList<LiveEncodedFrame>& output);
|
||||
QString error() const;
|
||||
QString name() const;
|
||||
static QList<int> available(); // реальная проба открытия и первого KEY; вызывать вне GUI
|
||||
Reference in new issue
Block a user