-
-
Notifications
You must be signed in to change notification settings - Fork 452
/
EncodePipelineSW.cpp
128 lines (107 loc) · 3.46 KB
/
EncodePipelineSW.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include "EncodePipelineSW.h"
#include <algorithm>
#include <chrono>
#include "alvr_server/Settings.h"
#include "alvr_server/Logger.h"
#include "ffmpeg_helper.h"
#include "FormatConverter.h"
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
}
namespace
{
const char * encoder(ALVR_CODEC codec)
{
switch (codec)
{
case ALVR_CODEC_H264:
return "libx264";
case ALVR_CODEC_H265:
return "libx265";
}
throw std::runtime_error("invalid codec " + std::to_string(codec));
}
}
alvr::EncodePipelineSW::EncodePipelineSW(Renderer *render, VkFrame &input_frame, uint32_t width, uint32_t height)
{
const auto& settings = Settings::Instance();
if (settings.m_codec == ALVR_CODEC_H265)
{
// TODO: Make it work?
throw std::runtime_error("HEVC is not supported by SW encoder");
}
auto codec_id = ALVR_CODEC(settings.m_codec);
const char * encoder_name = encoder(codec_id);
const AVCodec *codec = avcodec_find_encoder_by_name(encoder_name);
if (codec == nullptr)
{
throw std::runtime_error(std::string("Failed to find encoder ") + encoder_name);
}
encoder_ctx = avcodec_alloc_context3(codec);
if (not encoder_ctx)
{
throw std::runtime_error("failed to allocate " + std::string(encoder_name) + " encoder");
}
AVDictionary * opt = NULL;
switch (codec_id)
{
case ALVR_CODEC_H264:
encoder_ctx->profile = FF_PROFILE_H264_HIGH;
break;
case ALVR_CODEC_H265:
encoder_ctx->profile = settings.m_use10bitEncoder ? FF_PROFILE_HEVC_MAIN_10 : FF_PROFILE_HEVC_MAIN;
break;
}
switch (Settings::Instance().m_rateControlMode)
{
case ALVR_CBR:
av_dict_set(&opt, "nal-hrd", "cbr", 0);
break;
case ALVR_VBR:
av_dict_set(&opt, "nal-hrd", "vbr", 0);
break;
}
av_dict_set(&opt, "preset", "ultrafast", 0);
av_dict_set(&opt, "tune", "zerolatency", 0);
encoder_ctx->width = width;
encoder_ctx->height = height;
encoder_ctx->time_base = {1, (int)1e9};
encoder_ctx->framerate = AVRational{settings.m_refreshRate, 1};
encoder_ctx->sample_aspect_ratio = AVRational{1, 1};
encoder_ctx->pix_fmt = settings.m_use10bitEncoder && codec_id == ALVR_CODEC_H265 ? AV_PIX_FMT_YUV420P10 : AV_PIX_FMT_YUV420P;
encoder_ctx->max_b_frames = 0;
encoder_ctx->gop_size = 0;
encoder_ctx->bit_rate = settings.mEncodeBitrateMBs * 1000 * 1000;
encoder_ctx->rc_max_rate = encoder_ctx->bit_rate;
encoder_ctx->thread_type = FF_THREAD_SLICE;
encoder_ctx->thread_count = settings.m_swThreadCount;
int err = avcodec_open2(encoder_ctx, codec, &opt);
if (err < 0) {
throw alvr::AvException("Cannot open video encoder codec:", err);
}
encoder_frame = av_frame_alloc();
encoder_frame->width = width;
encoder_frame->height = height;
encoder_frame->format = encoder_ctx->pix_fmt;
av_frame_get_buffer(encoder_frame, 0);
rgbtoyuv = new RgbToYuv420(render, input_frame.image(), input_frame.imageInfo());
}
alvr::EncodePipelineSW::~EncodePipelineSW()
{
if (rgbtoyuv) {
delete rgbtoyuv;
}
av_frame_free(&encoder_frame);
}
void alvr::EncodePipelineSW::PushFrame(uint64_t targetTimestampNs, bool idr)
{
rgbtoyuv->Convert(encoder_frame->data, encoder_frame->linesize);
gpu_timestamp = rgbtoyuv->GetTimestamp();
encoder_frame->pict_type = idr ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_NONE;
encoder_frame->pts = targetTimestampNs;
int err;
if ((err = avcodec_send_frame(encoder_ctx, encoder_frame)) < 0) {
throw alvr::AvException("avcodec_send_frame failed:", err);
}
}